@natlibfi/melinda-record-matching 2.0.1-alpha.1 → 2.0.7-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/.github/CODEOWNERS +2 -0
  2. package/.github/dependabot.yml +40 -0
  3. package/.github/workflows/melinda-node-tests.yml +70 -0
  4. package/README.md +1 -1
  5. package/dist/candidate-search/candidate-search-utils.js +3 -6
  6. package/dist/candidate-search/candidate-search-utils.js.map +1 -1
  7. package/dist/candidate-search/index.js +115 -50
  8. package/dist/candidate-search/index.js.map +1 -1
  9. package/dist/candidate-search/index.spec.js +41 -48
  10. package/dist/candidate-search/index.spec.js.map +1 -1
  11. package/dist/candidate-search/query-list/bib.js +154 -64
  12. package/dist/candidate-search/query-list/bib.js.map +1 -1
  13. package/dist/candidate-search/query-list/bib.spec.js +1 -10
  14. package/dist/candidate-search/query-list/bib.spec.js.map +1 -1
  15. package/dist/candidate-search/query-list/index.js +4 -10
  16. package/dist/candidate-search/query-list/index.js.map +1 -1
  17. package/dist/index.js +498 -50
  18. package/dist/index.js.map +1 -1
  19. package/dist/index.spec.js +60 -34
  20. package/dist/index.spec.js.map +1 -1
  21. package/dist/match-detection/features/bib/all-source-ids.js +16 -19
  22. package/dist/match-detection/features/bib/all-source-ids.js.map +1 -1
  23. package/dist/match-detection/features/bib/authors.js +31 -35
  24. package/dist/match-detection/features/bib/authors.js.map +1 -1
  25. package/dist/match-detection/features/bib/bibliographic-level.js +3 -3
  26. package/dist/match-detection/features/bib/bibliographic-level.js.map +1 -1
  27. package/dist/match-detection/features/bib/host-component.js +3 -3
  28. package/dist/match-detection/features/bib/host-component.js.map +1 -1
  29. package/dist/match-detection/features/bib/index.js +28 -13
  30. package/dist/match-detection/features/bib/index.js.map +1 -1
  31. package/dist/match-detection/features/bib/index.spec.js +9 -13
  32. package/dist/match-detection/features/bib/index.spec.js.map +1 -1
  33. package/dist/match-detection/features/bib/isbn.js +28 -6
  34. package/dist/match-detection/features/bib/isbn.js.map +1 -1
  35. package/dist/match-detection/features/bib/issn.js +0 -4
  36. package/dist/match-detection/features/bib/issn.js.map +1 -1
  37. package/dist/match-detection/features/bib/language.js +69 -41
  38. package/dist/match-detection/features/bib/language.js.map +1 -1
  39. package/dist/match-detection/features/bib/media-type.js +83 -0
  40. package/dist/match-detection/features/bib/media-type.js.map +1 -0
  41. package/dist/match-detection/features/bib/melinda-id.js +0 -4
  42. package/dist/match-detection/features/bib/melinda-id.js.map +1 -1
  43. package/dist/match-detection/features/bib/melinda-identifier-factory.js +6 -17
  44. package/dist/match-detection/features/bib/melinda-identifier-factory.js.map +1 -1
  45. package/dist/match-detection/features/bib/other-standard-identifier.js +1 -4
  46. package/dist/match-detection/features/bib/other-standard-identifier.js.map +1 -1
  47. package/dist/match-detection/features/bib/publication-time-allow-cons-years-multi.js +256 -0
  48. package/dist/match-detection/features/bib/publication-time-allow-cons-years-multi.js.map +1 -0
  49. package/dist/match-detection/features/bib/publication-time-allow-cons-years.js +71 -0
  50. package/dist/match-detection/features/bib/publication-time-allow-cons-years.js.map +1 -0
  51. package/dist/match-detection/features/bib/publication-time.js +9 -8
  52. package/dist/match-detection/features/bib/publication-time.js.map +1 -1
  53. package/dist/match-detection/features/bib/record-type.js +6 -3
  54. package/dist/match-detection/features/bib/record-type.js.map +1 -1
  55. package/dist/match-detection/features/bib/standard-identifier-factory.js +129 -39
  56. package/dist/match-detection/features/bib/standard-identifier-factory.js.map +1 -1
  57. package/dist/match-detection/features/bib/title-version-original.js +79 -0
  58. package/dist/match-detection/features/bib/title-version-original.js.map +1 -0
  59. package/dist/match-detection/features/bib/title.js +29 -22
  60. package/dist/match-detection/features/bib/title.js.map +1 -1
  61. package/dist/match-detection/features/index.js +0 -4
  62. package/dist/match-detection/features/index.js.map +1 -1
  63. package/dist/match-detection/index.js +94 -47
  64. package/dist/match-detection/index.js.map +1 -1
  65. package/dist/match-detection/index.spec.js +25 -15
  66. package/dist/match-detection/index.spec.js.map +1 -1
  67. package/dist/matching-utils.js +63 -15
  68. package/dist/matching-utils.js.map +1 -1
  69. package/package.json +27 -24
  70. package/src/candidate-search/index.js +93 -31
  71. package/src/candidate-search/index.spec.js +27 -24
  72. package/src/candidate-search/query-list/bib.js +149 -22
  73. package/src/candidate-search/query-list/index.js +1 -1
  74. package/src/index.js +310 -38
  75. package/src/index.spec.js +49 -21
  76. package/src/match-detection/features/bib/all-source-ids.js +3 -3
  77. package/src/match-detection/features/bib/authors.js +23 -16
  78. package/src/match-detection/features/bib/bibliographic-level.js +1 -1
  79. package/src/match-detection/features/bib/host-component.js +1 -1
  80. package/src/match-detection/features/bib/index.js +4 -0
  81. package/src/match-detection/features/bib/index.spec.js +10 -2
  82. package/src/match-detection/features/bib/isbn.js +21 -2
  83. package/src/match-detection/features/bib/language.js +63 -22
  84. package/src/match-detection/features/bib/media-type.js +77 -0
  85. package/src/match-detection/features/bib/melinda-identifier-factory.js +2 -3
  86. package/src/match-detection/features/bib/other-standard-identifier.js +2 -0
  87. package/src/match-detection/features/bib/publication-time-allow-cons-years-multi.js +268 -0
  88. package/src/match-detection/features/bib/publication-time-allow-cons-years.js +67 -0
  89. package/src/match-detection/features/bib/publication-time.js +9 -4
  90. package/src/match-detection/features/bib/record-type.js +5 -1
  91. package/src/match-detection/features/bib/standard-identifier-factory.js +107 -22
  92. package/src/match-detection/features/bib/title-version-original.js +78 -0
  93. package/src/match-detection/features/bib/title.js +27 -7
  94. package/src/match-detection/index.js +60 -22
  95. package/src/match-detection/index.spec.js +18 -4
  96. package/src/matching-utils.js +62 -5
@@ -1,28 +1,22 @@
1
1
  "use strict";
2
2
 
3
3
  var _chai = require("chai");
4
-
5
4
  var _fixura = require("@natlibfi/fixura");
6
-
7
5
  var _fixugenHttpClient = _interopRequireDefault(require("@natlibfi/fixugen-http-client"));
8
-
9
6
  var _marcRecord = require("@natlibfi/marc-record");
10
-
7
+ var _melindaCommons = require("@natlibfi/melinda-commons");
11
8
  var _ = _interopRequireWildcard(require("."));
12
-
9
+ var _debug = _interopRequireDefault(require("debug"));
13
10
  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); }
14
-
15
11
  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; }
16
-
17
12
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
18
-
19
13
  /**
20
14
  *
21
15
  * @licstart The following is the entire license notice for the JavaScript code in this file.
22
16
  *
23
17
  * Melinda record matching modules for Javascript
24
18
  *
25
- * Copyright (C) 2020 University Of Helsinki (The National Library Of Finland)
19
+ * Copyright (C) 2020-2022 University Of Helsinki (The National Library Of Finland)
26
20
  *
27
21
  * This file is part of melinda-record-matching-js
28
22
  *
@@ -43,6 +37,8 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
43
37
  * for the JavaScript code in this file.
44
38
  *
45
39
  */
40
+
41
+ const debug = (0, _debug.default)('@natlibfi/melinda-record-matching:candidate-search:test');
46
42
  describe('candidate-search', () => {
47
43
  (0, _fixugenHttpClient.default)({
48
44
  callback,
@@ -51,84 +47,88 @@ describe('candidate-search', () => {
51
47
  fixura: {
52
48
  reader: _fixura.READERS.JSON
53
49
  }
54
- }); // eslint-disable-next-line max-statements
55
-
50
+ });
56
51
  async function callback({
57
52
  getFixture,
58
53
  factoryOptions,
59
54
  searchOptions,
60
- expectedFactoryError,
61
- expectedSearchError,
55
+ expectedFactoryError = false,
56
+ expectedSearchError = false,
62
57
  enabled = true
63
58
  }) {
64
59
  const url = 'http://foo.bar';
65
-
66
60
  if (!enabled) {
67
61
  return;
68
62
  }
69
-
70
63
  if (expectedFactoryError) {
71
64
  if (expectedFactoryError.isCandidateSearchError) {
72
- (0, _chai.expect)(() => (0, _.default)({ ...formatFactoryOptions(),
65
+ (0, _chai.expect)(() => (0, _.default)({
66
+ ...formatFactoryOptions(),
73
67
  url
74
68
  })).to.throw(_.CandidateSearchError, new RegExp(expectedFactoryError, 'u'));
75
69
  return;
76
70
  }
77
-
78
- (0, _chai.expect)(() => (0, _.default)({ ...formatFactoryOptions(),
71
+ (0, _chai.expect)(() => (0, _.default)({
72
+ ...formatFactoryOptions(),
79
73
  url
80
74
  })).to.throw(new RegExp(expectedFactoryError, 'u'));
81
75
  return;
82
76
  }
83
-
84
- const search = (0, _.default)({ ...formatFactoryOptions(),
77
+ const search = (0, _.default)({
78
+ ...formatFactoryOptions(),
85
79
  url
86
80
  });
87
81
  await iterate({
88
- searchOptions
82
+ searchOptions,
83
+ expectedSearchError
89
84
  });
90
-
91
85
  function formatFactoryOptions() {
92
- return { ...factoryOptions,
93
- maxRecordsPerRequest: 1,
86
+ debug(`Using factoryOptions: ${JSON.stringify(factoryOptions)}`);
87
+ return {
88
+ ...factoryOptions,
89
+ maxRecordsPerRequest: factoryOptions.maxRecordsPerRequest || 1,
90
+ maxServerResults: factoryOptions.maxServerResults || undefined,
94
91
  record: new _marcRecord.MarcRecord(factoryOptions.record, {
95
92
  subfieldValues: false
96
93
  })
97
94
  };
98
- } // eslint-disable-next-line max-statements
99
-
100
-
95
+ }
101
96
  async function iterate({
102
97
  searchOptions,
98
+ expectedSearchError,
99
+ expectedErrorStatus,
103
100
  count = 1
104
101
  }) {
105
102
  const expectedResults = getFixture(`expectedResults${count}.json`);
106
-
107
103
  if (expectedSearchError) {
108
- // eslint-disable-line functional/no-conditional-statement
104
+ // eslint-disable-line functional/no-conditional-statements
109
105
  try {
110
106
  await search(searchOptions);
111
107
  throw new Error('Expected an error');
112
108
  } catch (err) {
109
+ debug(`Got an error: ${err}`);
113
110
  (0, _chai.expect)(err).to.be.an('error');
114
- (0, _chai.expect)(err.message).to.match(new RegExp(expectedSearchError, 'u'));
111
+ const errorMessage = err instanceof _melindaCommons.Error ? err.payload.message : err.message;
112
+ const errorStatus = err instanceof _melindaCommons.Error ? err.status : undefined;
113
+ debug(`errorMessage: ${errorMessage}, errorStatus: ${errorStatus}`);
114
+ (0, _chai.expect)(errorMessage).to.match(new RegExp(expectedSearchError, 'u'));
115
+ if (expectedErrorStatus) {
116
+ (0, _chai.expect)(errorStatus).to.be(expectedErrorStatus);
117
+ return;
118
+ }
115
119
  return;
116
120
  }
117
121
  }
118
122
 
119
- const results = await search(searchOptions);
120
- (0, _chai.expect)(formatResults(results)).to.eql(expectedResults);
121
-
122
- if (results.records.length > 0) {
123
- return iterate({
124
- searchOptions: resultsToOptions(results),
125
- count: count + 1
126
- });
123
+ // eslint-disable-next-line functional/no-conditional-statements
124
+ if (!expectedSearchError) {
125
+ const results = await search(searchOptions);
126
+ (0, _chai.expect)(formatResults(results)).to.eql(expectedResults);
127
127
  }
128
-
129
128
  function formatResults(results) {
130
- // console.log(results); //eslint-disable-line
131
- return { ...results,
129
+ debug(results);
130
+ return {
131
+ ...results,
132
132
  records: results.records.map(({
133
133
  record,
134
134
  id
@@ -138,13 +138,6 @@ describe('candidate-search', () => {
138
138
  }))
139
139
  };
140
140
  }
141
-
142
- function resultsToOptions(results) {
143
- return Object.entries(results).filter(([k]) => k === 'records' === false) // If key is 'records' return false
144
- .reduce((acc, [k, v]) => ({ ...acc,
145
- [k]: v
146
- }), {});
147
- }
148
141
  }
149
142
  }
150
143
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/candidate-search/index.spec.js"],"names":["describe","callback","path","__dirname","recurse","fixura","reader","READERS","JSON","getFixture","factoryOptions","searchOptions","expectedFactoryError","expectedSearchError","enabled","url","isCandidateSearchError","formatFactoryOptions","to","throw","CandidateSearchError","RegExp","search","iterate","maxRecordsPerRequest","record","MarcRecord","subfieldValues","count","expectedResults","Error","err","be","an","message","match","results","formatResults","eql","records","length","resultsToOptions","map","id","toObject","Object","entries","filter","k","reduce","acc","v"],"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 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 createSearchInterface, {CandidateSearchError} from '.';\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, expectedSearchError, 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});\n\n function formatFactoryOptions() {\n return {\n ...factoryOptions,\n maxRecordsPerRequest: 1,\n record: new MarcRecord(factoryOptions.record, {subfieldValues: false})\n };\n }\n\n // eslint-disable-next-line max-statements\n async function iterate({searchOptions, 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 expect(err).to.be.an('error');\n expect(err.message).to.match(new RegExp(expectedSearchError, 'u'));\n return;\n }\n }\n\n const results = await search(searchOptions);\n\n expect(formatResults(results)).to.eql(expectedResults);\n\n if (results.records.length > 0) {\n return iterate({\n searchOptions: resultsToOptions(results),\n count: count + 1\n });\n }\n\n function formatResults(results) {\n // console.log(results); //eslint-disable-line\n return {\n ...results,\n records: results.records.map(({record, id}) => ({id, record: record.toObject()}))\n };\n }\n\n function resultsToOptions(results) {\n return Object.entries(results)\n .filter(([k]) => k === 'records' === false) // If key is 'records' return false\n .reduce((acc, [k, v]) => ({...acc, [k]: v}), {});\n }\n }\n }\n});\n"],"file":"index.spec.js"}
1
+ {"version":3,"file":"index.spec.js","names":["_chai","require","_fixura","_fixugenHttpClient","_interopRequireDefault","_marcRecord","_melindaCommons","_","_interopRequireWildcard","_debug","_getRequireWildcardCache","nodeInterop","WeakMap","cacheBabelInterop","cacheNodeInterop","obj","__esModule","default","cache","has","get","newObj","hasPropertyDescriptor","Object","defineProperty","getOwnPropertyDescriptor","key","prototype","hasOwnProperty","call","desc","set","debug","createDebugLogger","describe","generateTests","callback","path","__dirname","recurse","fixura","reader","READERS","JSON","getFixture","factoryOptions","searchOptions","expectedFactoryError","expectedSearchError","enabled","url","isCandidateSearchError","expect","createSearchInterface","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"],"sources":["../../src/candidate-search/index.spec.js"],"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 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 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-statements\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-statements\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);\n return {\n ...results,\n records: results.records.map(({record, id}) => ({id, record: record.toObject()}))\n };\n }\n\n }\n }\n});\n"],"mappings":"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"}
@@ -3,27 +3,24 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
+ exports.bibAuthors = bibAuthors;
6
7
  exports.bibHostComponents = bibHostComponents;
7
8
  exports.bibMelindaIds = bibMelindaIds;
9
+ exports.bibPublishers = bibPublishers;
8
10
  exports.bibSourceIds = bibSourceIds;
9
11
  exports.bibStandardIdentifiers = bibStandardIdentifiers;
10
12
  exports.bibTitle = bibTitle;
11
-
12
13
  var _debug = _interopRequireDefault(require("debug"));
13
-
14
14
  var _candidateSearchUtils = require("../candidate-search-utils");
15
-
16
15
  var _matchingUtils = require("../../matching-utils");
17
-
18
16
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
19
-
20
17
  /**
21
18
  *
22
19
  * @licstart The following is the entire license notice for the JavaScript code in this file.
23
20
  *
24
21
  * Melinda record matching modules for Javascript
25
22
  *
26
- * Copyright (C) 2020 University Of Helsinki (The National Library Of Finland)
23
+ * Copyright (C) 2020-2022 University Of Helsinki (The National Library Of Finland)
27
24
  *
28
25
  * This file is part of melinda-record-matching-js
29
26
  *
@@ -44,6 +41,7 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
44
41
  * for the JavaScript code in this file.
45
42
  *
46
43
  */
44
+
47
45
  function bibSourceIds(record) {
48
46
  /* Melinda's SRU-index melinda.sourceid includes source IDs from SID fields in Melinda records
49
47
  SID-fields in Melinda have sf $c with local id and sf $b with a code for the local db:
@@ -62,36 +60,33 @@ function bibSourceIds(record) {
62
60
  afterwards. SIDs are also not reliably maintained. A record might or might not have a SID for a local db
63
61
  after the matching record is removed from the local db.
64
62
  */
63
+
65
64
  const debug = (0, _debug.default)('@natlibfi/melinda-record-matching:candidate-search:query:source-ids');
66
- const debugData = debug.extend('data'); //const debugInfo = debug.extend('info');
65
+ const debugData = debug.extend('data');
66
+ //const debugInfo = debug.extend('info');
67
67
 
68
68
  debug(`Creating queries for sourceid's`);
69
69
  const fSids = record.get('SID');
70
70
  debugData(`SID-fields (${fSids.length}): ${JSON.stringify(fSids)}`);
71
71
  return fSids.length > 0 ? toSidQueries(fSids) : [];
72
-
73
72
  function toSidQueries(fSids) {
74
73
  debug(`Creating actual queries for sourceid's`);
75
74
  const sidStrings = getSidStrings(fSids);
76
-
77
75
  if (sidStrings.length < 1) {
78
76
  debug(`No identifiers found, no queries created.`);
79
77
  return [];
80
78
  }
81
-
82
79
  const sidQueries = (0, _candidateSearchUtils.toQueries)(sidStrings, 'melinda.sourceid');
83
80
  return sidQueries;
84
-
85
81
  function getSidStrings(fSids) {
86
- debug(`Getting Sid strings from SID fields`); // Map SID fields to valid sidStrings, filter out empty strings
82
+ debug(`Getting Sid strings from SID fields`);
87
83
 
84
+ // Map SID fields to valid sidStrings, filter out empty strings
88
85
  const sidStrings = fSids.map(toSidString).filter(nonEmptySid => nonEmptySid);
89
86
  return sidStrings;
90
-
91
87
  function toSidString(field) {
92
88
  debug(`Getting string from a field`);
93
89
  return (0, _matchingUtils.validateSidFieldSubfieldCounts)(field) ? createSidString(field) : '';
94
-
95
90
  function createSidString(field) {
96
91
  debug(`Creating string from a field`);
97
92
  const [sfC] = (0, _matchingUtils.getSubfieldValues)(field, 'c');
@@ -101,148 +96,243 @@ function bibSourceIds(record) {
101
96
  debugData(`${JSON.stringify(sfC)} + ${JSON.stringify(sfB)}`);
102
97
  return cleanedSfC.concat(cleanedSfB);
103
98
  }
104
-
105
99
  function removeSourcePrefix(subfieldValue) {
106
100
  const sourcePrefixRegex = /^(?<sourcePrefix>\([A-Za-z0-9-]+\))(?<id>.+)$/u;
107
- const normalizedValue = subfieldValue.replace(sourcePrefixRegex, '$<id>');
101
+ const normalizedValue = (0, _matchingUtils.testStringOrNumber)(subfieldValue) ? String(subfieldValue).replace(sourcePrefixRegex, '$<id>') : '';
108
102
  debugData(`Normalized ${subfieldValue} to ${normalizedValue}`);
109
103
  return normalizedValue;
110
104
  }
111
-
112
105
  function normalizeSidSubfieldValue(subfieldValue) {
113
106
  debugData(`Normalizing ${subfieldValue}`);
114
107
  const normalizeAwayRegex = /[- ]/u;
115
- return subfieldValue.replace(normalizeAwayRegex, '');
108
+ return (0, _matchingUtils.testStringOrNumber)(subfieldValue) ? String(subfieldValue).replace(normalizeAwayRegex, '') : '';
116
109
  }
117
110
  }
118
111
  }
119
112
  }
120
113
  }
121
-
122
114
  function bibMelindaIds(record) {
123
115
  // Melinda's SRU-index melinda.melindaid includes f001 controlnumbers and old Melinda-IDs from f035z's for all non-deleted Melinda-records
116
+
124
117
  const debug = (0, _debug.default)('@natlibfi/melinda-record-matching:candidate-search:query:bibMelindaIds');
125
118
  const debugData = debug.extend('data');
126
- debug(`Creating queries for MelindaIds`); // Note: Melinda-ID's for search queries are created just from records f035a's and f035z's
119
+ debug(`Creating queries for MelindaIds`);
120
+
121
+ // Note: Melinda-ID's for search queries are created just from records f035a's and f035z's
127
122
  // Both (FI-MELINDA)- and FCC-prefixed forms are found
128
123
  // f001 controlnumber is not currently included, even if record's f003 is FI-MELINDA
129
-
130
124
  const melindaIds = (0, _matchingUtils.getMelindaIdsF035)(record);
131
125
  debugData(`Unique identifiers (${melindaIds.length}): ${JSON.stringify(melindaIds)}`);
132
-
133
126
  if (melindaIds.length < 1) {
134
127
  debug(`No identifiers found, no queries created.`);
135
128
  return [];
136
129
  }
137
-
138
130
  return (0, _candidateSearchUtils.toQueries)(melindaIds, 'melinda.melindaid');
139
- } // bibHostComponents returns host id from the first subfield $w of first field f773, see test-fixtures 04 and 05
140
- // bibHostComponents should search all 773 $ws for possible host id, but what should it do in case of multiple host ids?
141
-
131
+ }
142
132
 
133
+ // bibHostComponents returns host id from the first subfield $w of first field f773, see test-fixtures 04 and 05
134
+ // bibHostComponents should search all 773 $ws for possible host id, but what should it do in case of multiple host ids?
143
135
  function bibHostComponents(record) {
144
136
  const id = getHostId();
145
- return id ? [`melinda.partsofhost=${id}`] : [];
146
-
137
+ return (0, _matchingUtils.testStringOrNumber)(id) ? [`melinda.partsofhost=${id}`] : [];
147
138
  function getHostId() {
148
139
  const [field] = record.get(/^773$/u);
149
-
150
140
  if (field) {
151
141
  const {
152
142
  value
153
143
  } = field.subfields.find(({
154
144
  code
155
145
  }) => code === 'w') || {};
156
-
157
- if (value && /^\(FI-MELINDA\)/u.test(value)) {
158
- return value.replace(/^\(FI-MELINDA\)/u, '');
146
+ if ((0, _matchingUtils.testStringOrNumber)(value) && /^\(FI-MELINDA\)/u.test(String(value))) {
147
+ return String(value).replace(/^\(FI-MELINDA\)/u, '');
159
148
  }
160
-
161
- if (value && /^\(FIN01\)/u.test(value)) {
162
- return value.replace(/^\(FIN01\)/u, '');
149
+ if ((0, _matchingUtils.testStringOrNumber)(value) && /^\(FIN01\)/u.test(String(value))) {
150
+ return String(value).replace(/^\(FIN01\)/u, '');
163
151
  }
164
-
165
152
  return false;
166
153
  }
167
-
168
154
  return false;
169
155
  }
170
- } // SRU search dc.title with a search phrase starting with ^ maps currently in Melinda to
156
+ }
157
+
158
+ // SRU search dc.title with a search phrase starting with ^ maps currently in Melinda to
171
159
  // (probably) to *headings* index TIT
160
+ // - Aleph cannot currently handle headings searches starting with a boolean - in these cases use word search
161
+
172
162
  // Headings index TIT drops articles etc. from the start of the title according to the filing indicator
173
163
  // Currently filing indicator is not implemented - if the title starts with an article and the Melinda
174
164
  // record is correctly catalogued using a filing indicator -> dc.title search won't match
175
165
 
176
-
177
166
  function bibTitle(record) {
178
167
  const title = getTitle();
179
-
180
- if (title) {
181
- const formatted = title.replace(/[^\w\s\p{Alphabetic}]/gu, '') // Clean up concurrent spaces from fe. subfield changes
182
- .replace(/ +/gu, ' ').trim().slice(0, 30).trim(); // Prevent too many matches by having a minimum length requirement
183
-
184
- return formatted.length >= 5 ? [`dc.title="^${formatted}*"`] : [];
168
+ const booleanStartWords = ['and', 'or', 'nor', 'not'];
169
+ if ((0, _matchingUtils.testStringOrNumber)(title)) {
170
+ const formatted = String(title).replace(/[^\w\s\p{Alphabetic}]/gu, '')
171
+ // Clean up concurrent spaces from fe. subfield changes
172
+ .replace(/ +/gu, ' ').trim().slice(0, 30).trim();
173
+
174
+ // use word search for titles starting with a boolean
175
+ const useWordSearch = booleanStartWords.some(word => formatted.toLowerCase().startsWith(word));
176
+ // Prevent too many matches by having a minimum length
177
+ // Note that currently this fails matching if there are no matches from previous matchers
178
+ if (formatted.length >= 5) {
179
+ return [`dc.title="${useWordSearch ? '' : '^'}${formatted}*"`];
180
+ }
181
+ // use word search without ending * also in combination searches to avoid SRU-server crashes [MRA-189]
182
+ return addAuthorsToSearch(`dc.title="${formatted}"`);
185
183
  }
186
-
187
184
  return [];
185
+ function addAuthorsToSearch(titleQuery) {
186
+ const [authorQuery] = bibAuthors(record);
187
+ if (authorQuery !== undefined) {
188
+ return [`${authorQuery} AND ${titleQuery}`];
189
+ }
190
+ return addPublisherToSearch(titleQuery);
191
+ //return [];
192
+ }
188
193
 
194
+ function addPublisherToSearch(query) {
195
+ const [publisherQuery] = bibPublishers(record);
196
+ if (publisherQuery !== undefined) {
197
+ return [`${publisherQuery} AND ${query}`];
198
+ }
199
+ return [];
200
+ }
189
201
  function getTitle() {
190
202
  const [field] = record.get(/^245$/u);
191
-
192
203
  if (field) {
193
- return field.subfields.filter(({
204
+ const titleString = field.subfields
205
+ //.filter(({code}) => ['a', 'b', 'n', 'p'].includes(code))
206
+ .filter(({
194
207
  code
195
- }) => ['a', 'b'].includes(code)).map(({
208
+ }) => ['a', 'b'].includes(code))
209
+ //.filter(({code}) => ['a'].includes(code))
210
+ .map(({
196
211
  value
197
- }) => value) // In Melinda's index subfield separators are indexed as ' '
212
+ }) => (0, _matchingUtils.testStringOrNumber)(value) ? String(value) : '').filter(value => value)
213
+ // In Melinda's index subfield separators are indexed as ' '
198
214
  .join(' ');
215
+ return titleString;
199
216
  }
200
-
201
217
  return false;
202
218
  }
203
- } // eslint-disable-next-line max-statements
219
+ }
220
+ function bibAuthors(record) {
221
+ const debug = (0, _debug.default)('@natlibfi/melinda-record-matching:candidate-search:query:bibAuthors');
222
+ const debugData = debug.extend('data');
223
+ debug(`Creating query for the first author`);
224
+ //debugData(record);
225
+
226
+ const author = getAuthor(record);
227
+ const booleanStartWords = ['and', 'or', 'nor', 'not'];
228
+ if ((0, _matchingUtils.testStringOrNumber)(author)) {
229
+ const formatted = String(author).replace(/[^\w\s\p{Alphabetic}]/gu, '')
230
+ // Clean up concurrent spaces from fe. subfield changes
231
+ .replace(/ +/gu, ' ').trim().slice(0, 30).trim();
232
+
233
+ // use word search for authors starting with a boolean
234
+ const useWordSearch = booleanStartWords.some(word => formatted.toLowerCase().startsWith(word));
235
+ // Prevent too many matches by having a minimum length
236
+ debugData(`Author string: ${formatted}`);
237
+ if (formatted.length >= 5) {
238
+ return [`dc.author="${useWordSearch ? '' : '^'}${formatted}*"`];
239
+ }
240
+ //if (formatted) {
241
+ // return [`dc.author="${formatted}"`];
242
+ //}
243
+ return [];
244
+ }
245
+ return [];
246
+ function getAuthor(record) {
247
+ //debugData(record);
248
+ // eslint-disable-next-line prefer-named-capture-group
249
+ const [field] = record.get(/^(100)|(110)|(111)|(700)|(710)|(711)$/u);
250
+ //debugData(field);
204
251
 
252
+ if (field) {
253
+ const authorString = field.subfields.filter(({
254
+ code
255
+ }) => ['a'].includes(code)).map(({
256
+ value
257
+ }) => (0, _matchingUtils.testStringOrNumber)(value) ? String(value) : '').filter(value => value)
258
+ // In Melinda's index subfield separators are indexed as ' '
259
+ .join(' ');
260
+ return authorString;
261
+ }
262
+ return false;
263
+ }
264
+ }
265
+ function bibPublishers(record) {
266
+ const debug = (0, _debug.default)('@natlibfi/melinda-record-matching:candidate-search:query:bibPublishers');
267
+ const debugData = debug.extend('data');
268
+ debug(`Creating query for the first publisher`);
269
+ //debugData(record);
270
+
271
+ const publisher = getPublisher(record);
272
+ if ((0, _matchingUtils.testStringOrNumber)(publisher)) {
273
+ const formatted = String(publisher).replace(/[^\w\s\p{Alphabetic}]/gu, '')
274
+ // Clean up concurrent spaces from fe. subfield changes
275
+ .replace(/ +/gu, ' ').trim().slice(0, 30).trim();
276
+ debugData(`Publisher string: ${formatted}`);
277
+ // use non-wildcard word search from dc.publisher
278
+ return [`dc.publisher="${formatted}"`];
279
+ }
280
+ return [];
281
+ function getPublisher(record) {
282
+ //debugData(record);
283
+ const [field] = record.get(/^(?:260)|(?:264)$/u);
284
+ //debugData(field);
205
285
 
286
+ if (field) {
287
+ const publisherString = field.subfields.filter(({
288
+ code
289
+ }) => ['b'].includes(code)).map(({
290
+ value
291
+ }) => (0, _matchingUtils.testStringOrNumber)(value) ? String(value) : '').filter(value => value)
292
+ // In Melinda's index subfield separators are indexed as ' '
293
+ .join(' ');
294
+ return publisherString;
295
+ }
296
+ return false;
297
+ }
298
+ }
206
299
  function bibStandardIdentifiers(record) {
207
300
  const debug = (0, _debug.default)('@natlibfi/melinda-record-matching:candidate-search:query:bibStandardIdentifiers');
208
301
  const debugData = debug.extend('data');
209
302
  debug(`Creating queries for standard identifiers`);
303
+
304
+ // DEVELOP: should we query also f015 and f028?
305
+
210
306
  const fields = record.get(/^(?<def>020|022|024)$/u);
211
307
  const identifiers = [].concat(...fields.map(toIdentifiers));
212
308
  const uniqueIdentifiers = [...new Set(identifiers)];
213
309
  debugData(`Standard identifier fields: ${JSON.stringify(fields)}`);
214
310
  debugData(`Identifiers (${identifiers.length}): ${JSON.stringify(identifiers)}`);
215
311
  debugData(`Unique identifiers (${uniqueIdentifiers.length}): ${JSON.stringify(uniqueIdentifiers)}`);
216
-
217
312
  if (uniqueIdentifiers.length < 1) {
218
313
  debug(`No identifiers found, no queries created.`);
219
314
  return [];
220
315
  }
221
-
222
316
  return (0, _candidateSearchUtils.toQueries)(uniqueIdentifiers, 'dc.identifier');
223
-
224
317
  function toIdentifiers({
225
318
  tag,
226
319
  subfields
227
320
  }) {
228
321
  const issnIsbnReqExp = /^[A-Za-z0-9-]+$/u;
229
322
  const otherIdReqExp = /^[A-Za-z0-9-:]+$/u;
230
-
231
323
  if (tag === '022') {
232
- return subfields.filter(sub => ['a', 'z', 'y'].includes(sub.code) && issnIsbnReqExp.test(sub.value) && sub.value !== undefined).map(({
324
+ return subfields.filter(sub => ['a', 'z', 'y'].includes(sub.code) && (0, _matchingUtils.testStringOrNumber)(sub.value) && issnIsbnReqExp.test(String(sub.value))).map(({
233
325
  value
234
- }) => value);
326
+ }) => String(value));
235
327
  }
236
-
237
328
  if (tag === '020') {
238
- return subfields.filter(sub => ['a', 'z'].includes(sub.code) && issnIsbnReqExp.test(sub.value) && sub.value !== undefined).map(({
329
+ return subfields.filter(sub => ['a', 'z'].includes(sub.code) && (0, _matchingUtils.testStringOrNumber)(sub.value) && issnIsbnReqExp.test(String(sub.value))).map(({
239
330
  value
240
- }) => value);
331
+ }) => String(value));
241
332
  }
242
-
243
- return subfields.filter(sub => ['a', 'z'].includes(sub.code) && otherIdReqExp.test(sub.value) && sub.value !== undefined).map(({
333
+ return subfields.filter(sub => ['a', 'z'].includes(sub.code) && (0, _matchingUtils.testStringOrNumber)(sub.value) && otherIdReqExp.test(String(sub.value))).map(({
244
334
  value
245
- }) => value);
335
+ }) => String(value));
246
336
  }
247
337
  }
248
338
  //# sourceMappingURL=bib.js.map