@natlibfi/marc-record-validators-melinda 12.0.6 → 12.0.7
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/cyrillux.js +11 -11
- package/dist/cyrillux.js.map +2 -2
- package/dist/dataProvenanceUtils.js +19 -0
- package/dist/dataProvenanceUtils.js.map +7 -0
- package/dist/index.js +3 -1
- package/dist/index.js.map +2 -2
- package/dist/merge-fields/controlSubfields.js.map +2 -2
- package/dist/merge-fields/counterpartField.js +149 -152
- package/dist/merge-fields/counterpartField.js.map +3 -3
- package/dist/merge-fields/dataProvenance.js +6 -20
- package/dist/merge-fields/dataProvenance.js.map +2 -2
- package/dist/merge-fields/index.js +1 -1
- package/dist/merge-fields/index.js.map +2 -2
- package/dist/merge-fields/mergableIndicator.js +1 -2
- package/dist/merge-fields/mergableIndicator.js.map +2 -2
- package/dist/merge-fields/mergeConfig.js +2 -0
- package/dist/merge-fields/mergeConfig.js.map +2 -2
- package/dist/merge-fields/mergeConstraints.js +35 -32
- package/dist/merge-fields/mergeConstraints.js.map +3 -3
- package/dist/merge-fields/mergeField.js +4 -3
- package/dist/merge-fields/mergeField.js.map +2 -2
- package/dist/merge-fields/mergeOrAddSubfield.js +8 -7
- package/dist/merge-fields/mergeOrAddSubfield.js.map +2 -2
- package/dist/merge-fields/mergeSubfield.js +5 -1
- package/dist/merge-fields/mergeSubfield.js.map +2 -2
- package/dist/merge-fields/worldKnowledge.js +52 -0
- package/dist/merge-fields/worldKnowledge.js.map +2 -2
- package/dist/merge-fields.test.js +2 -2
- package/dist/merge-fields.test.js.map +2 -2
- package/dist/normalize-dashes.js +2 -2
- package/dist/normalize-dashes.js.map +2 -2
- package/dist/normalizeFieldForComparison.js +8 -14
- package/dist/normalizeFieldForComparison.js.map +2 -2
- package/dist/prepublicationUtils.js +1 -1
- package/dist/prepublicationUtils.js.map +2 -2
- package/dist/punctuation2.js +10 -10
- package/dist/punctuation2.js.map +2 -2
- package/dist/removeDuplicateDataFields.js +1 -24
- package/dist/removeDuplicateDataFields.js.map +2 -2
- package/dist/removeInferiorDataFields.js +3 -2
- package/dist/removeInferiorDataFields.js.map +2 -2
- package/dist/sortSubfields.js +19 -19
- package/dist/sortSubfields.js.map +2 -2
- package/dist/subfield6Utils.js +0 -1
- package/dist/subfield6Utils.js.map +2 -2
- package/dist/subfield8Utils.js +0 -5
- package/dist/subfield8Utils.js.map +2 -2
- package/dist/utils.js +29 -3
- package/dist/utils.js.map +2 -2
- package/package.json +4 -4
- package/src/cyrillux.js +11 -11
- package/src/dataProvenanceUtils.js +21 -0
- package/src/index.js +3 -1
- package/src/merge-fields/controlSubfields.js +0 -1
- package/src/merge-fields/counterpartField.js +191 -290
- package/src/merge-fields/dataProvenance.js +8 -25
- package/src/merge-fields/index.js +1 -1
- package/src/merge-fields/mergableIndicator.js +1 -2
- package/src/merge-fields/mergeConfig.js +2 -1
- package/src/merge-fields/mergeConstraints.js +39 -34
- package/src/merge-fields/mergeField.js +4 -7
- package/src/merge-fields/mergeOrAddSubfield.js +8 -7
- package/src/merge-fields/mergeSubfield.js +11 -2
- package/src/merge-fields/worldKnowledge.js +72 -3
- package/src/merge-fields.test.js +2 -2
- package/src/normalize-dashes.js +2 -2
- package/src/normalizeFieldForComparison.js +19 -20
- package/src/prepublicationUtils.js +1 -1
- package/src/punctuation2.js +10 -10
- package/src/removeDuplicateDataFields.js +24 -24
- package/src/removeInferiorDataFields.js +3 -2
- package/src/sortSubfields.js +19 -19
- package/src/subfield6Utils.js +1 -1
- package/src/subfield8Utils.js +5 -5
- package/src/utils.js +39 -12
- package/test-fixtures/cyrillux/f14/expectedResult.json +32 -0
- package/test-fixtures/cyrillux/f14/metadata.json +10 -0
- package/test-fixtures/cyrillux/f14/record.json +14 -0
- package/test-fixtures/merge-fields/f042_01/expectedResult.json +12 -0
- package/test-fixtures/merge-fields/f042_01/metadata.json +6 -0
- package/test-fixtures/merge-fields/f042_01/record.json +13 -0
- package/test-fixtures/merge-fields/f06/expectedResult.json +42 -0
- package/test-fixtures/merge-fields/f06/metadata.json +6 -0
- package/test-fixtures/merge-fields/f06/record.json +41 -0
- package/test-fixtures/merge-fields/f07/expectedResult.json +18 -0
- package/test-fixtures/merge-fields/f07/metadata.json +6 -0
- package/test-fixtures/merge-fields/f07/record.json +18 -0
- package/test-fixtures/merge-fields/f08/expectedResult.json +12 -0
- package/test-fixtures/merge-fields/f08/metadata.json +7 -0
- package/test-fixtures/merge-fields/f08/record.json +10 -0
- package/test-fixtures/merge-fields/f09/expectedResult.json +14 -0
- package/test-fixtures/merge-fields/f09/metadata.json +6 -0
- package/test-fixtures/merge-fields/f09/record.json +14 -0
- package/test-fixtures/merge-fields/f10/expectedResult.json +25 -0
- package/test-fixtures/merge-fields/f10/metadata.json +6 -0
- package/test-fixtures/merge-fields/f10/record.json +25 -0
- package/test-fixtures/merge-fields/f11/expectedResult.json +40 -0
- package/test-fixtures/merge-fields/f11/metadata.json +7 -0
- package/test-fixtures/merge-fields/f11/record.json +50 -0
- package/test-fixtures/merge-fields/f12/expectedResult.json +17 -0
- package/test-fixtures/merge-fields/f12/metadata.json +6 -0
- package/test-fixtures/merge-fields/f12/record.json +25 -0
- package/test-fixtures/merge-fields/f13/expectedResult.json +18 -0
- package/test-fixtures/merge-fields/f13/metadata.json +6 -0
- package/test-fixtures/merge-fields/f13/record.json +28 -0
- package/test-fixtures/merge-fields/f14/expectedResult.json +25 -0
- package/test-fixtures/merge-fields/f14/metadata.json +6 -0
- package/test-fixtures/merge-fields/f14/record.json +25 -0
- package/test-fixtures/merge-fields/f300_01/expectedResult.json +9 -0
- package/test-fixtures/merge-fields/f300_01/metadata.json +6 -0
- package/test-fixtures/merge-fields/f300_01/record.json +8 -0
- package/test-fixtures/merge-fields/f300_02/expectedResult.json +13 -0
- package/test-fixtures/merge-fields/f300_02/metadata.json +6 -0
- package/test-fixtures/merge-fields/f300_02/record.json +16 -0
- package/test-fixtures/merge-fields/f490_01/expectedResult.json +13 -0
- package/test-fixtures/merge-fields/f490_01/metadata.json +6 -0
- package/test-fixtures/merge-fields/f490_01/record.json +16 -0
- package/test-fixtures/remove-inferior-datafields/f17/expectedResult.json +11 -0
- package/test-fixtures/remove-inferior-datafields/f17/metadata.json +5 -0
- package/test-fixtures/remove-inferior-datafields/f17/record.json +15 -0
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import createDebugLogger from "debug";
|
|
2
|
-
import { fieldHasSubfield, fieldHasNSubfields, fieldHasMultipleSubfields, fieldToString, nvdebug, removeCopyright } from "../utils.js";
|
|
2
|
+
import { fieldHasSubfield, fieldHasNSubfields, fieldHasMultipleSubfields, fieldToString, nvdebug, removeCopyright, tagIsRepeatable } from "../utils.js";
|
|
3
3
|
import { cloneAndNormalizeFieldForComparison, cloneAndRemovePunctuation } from "../normalizeFieldForComparison.js";
|
|
4
4
|
import { normalizeControlSubfieldValue } from "../normalize-identifiers.js";
|
|
5
5
|
import { getMergeConstraintsForTag } from "./mergeConstraints.js";
|
|
6
6
|
import { controlSubfieldsPermitMerge } from "./controlSubfields.js";
|
|
7
7
|
import { mergableIndicator1, mergableIndicator2 } from "./mergableIndicator.js";
|
|
8
8
|
import { partsAgree } from "../normalizeSubfieldValueForComparison.js";
|
|
9
|
-
import { normalizeForSamenessCheck, valueCarriesMeaning } from "./worldKnowledge.js";
|
|
9
|
+
import { getSynonym, normalizeForSamenessCheck, valueCarriesMeaning } from "./worldKnowledge.js";
|
|
10
10
|
import { provenanceSubfieldsPermitMerge } from "./dataProvenance.js";
|
|
11
11
|
const debug = createDebugLogger("@natlibfi/marc-record-validators-melinda:mergeField:counterpart");
|
|
12
12
|
const debugDev = debug.extend("dev");
|
|
13
|
-
const irrelevantSubfieldsInNameAndTitlePartComparison = "5689";
|
|
14
13
|
const counterpartRegexps = {
|
|
15
14
|
// NB! tag is from source!
|
|
16
15
|
// Note that in the normal case, all source 1XX fields have been converted to 7XX fields.
|
|
@@ -47,7 +46,7 @@ export function splitToNameAndQualifier(value) {
|
|
|
47
46
|
}
|
|
48
47
|
return [value, void 0];
|
|
49
48
|
}
|
|
50
|
-
|
|
49
|
+
function splitToNameAndQualifierAndProcessName(name) {
|
|
51
50
|
const [qualifierlessName, qualifier] = splitToNameAndQualifier(name);
|
|
52
51
|
const [prefix, basename, suffix] = stripPrefixAndSuffix(qualifierlessName);
|
|
53
52
|
return { name: getBestName(basename).toLowerCase(), prefix, suffix, qualifier };
|
|
@@ -120,78 +119,22 @@ function corporateNamesAgree(value1, value2, tag, subfieldCode) {
|
|
|
120
119
|
}
|
|
121
120
|
return true;
|
|
122
121
|
}
|
|
123
|
-
function pairableValue(tag, subfieldCode, value1, value2) {
|
|
124
|
-
if (withAndWithoutQualifierAgree(value1, value2, tag, subfieldCode)) {
|
|
125
|
-
return value1;
|
|
126
|
-
}
|
|
127
|
-
if (partsAgree(value1, value2, tag, subfieldCode) || corporateNamesAgree(value1, value2, tag, subfieldCode)) {
|
|
128
|
-
return value1;
|
|
129
|
-
}
|
|
130
|
-
return void 0;
|
|
131
|
-
}
|
|
132
122
|
function counterpartExtraNormalize(tag, subfieldCode, value) {
|
|
133
123
|
value = value.replace(/(\S)(?:,|\.|\?|!|\. -| *:| *;| =| \/)$/u, "$1");
|
|
134
124
|
value = value.replace(/^\(([^()]+)\)$/u, "$1");
|
|
135
125
|
value = value.replace(/^\[([^[\]]+)\]$/u, "$1");
|
|
136
|
-
|
|
126
|
+
if (tag === "260" && subfieldCode === "c") {
|
|
127
|
+
value = removeCopyright(value);
|
|
128
|
+
}
|
|
137
129
|
value = value.replace(/http:\/\//ug, "https://");
|
|
130
|
+
value = getSynonym(tag, subfieldCode, value);
|
|
138
131
|
value = normalizeForSamenessCheck(tag, subfieldCode, value);
|
|
139
132
|
return value;
|
|
140
133
|
}
|
|
141
|
-
function
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
function optionalSubfieldComparison(originalBaseField, originalSourceField, keySubfieldsAsString) {
|
|
146
|
-
const field1 = cloneAndNormalizeFieldForComparison(originalBaseField);
|
|
147
|
-
const field2 = cloneAndNormalizeFieldForComparison(originalSourceField);
|
|
148
|
-
if (keySubfieldsAsString === null) {
|
|
149
|
-
return fieldToString(field1).substring(6) === fieldToString(field2).substring(6);
|
|
150
|
-
}
|
|
151
|
-
const subfieldArray = keySubfieldsAsString.split("");
|
|
152
|
-
if (subfieldArray.length > 0 && !subfieldArray.some((sfCode) => hasCommonNominator(sfCode))) {
|
|
153
|
-
return false;
|
|
154
|
-
}
|
|
155
|
-
return subfieldArray.every((subfieldCode) => testOptionalSubfield(originalBaseField.tag, subfieldCode));
|
|
156
|
-
function hasCommonNominator(subfieldCode) {
|
|
157
|
-
const subfields1 = field1.subfields.filter((subfield) => subfield.code === subfieldCode && valueCarriesMeaning(field1.tag, subfield.code, subfield.value));
|
|
158
|
-
const subfields2 = field2.subfields.filter((subfield) => subfield.code === subfieldCode && valueCarriesMeaning(field2.tag, subfield.code, subfield.value));
|
|
159
|
-
return subfields1.length > 0 && subfields2.length > 0;
|
|
160
|
-
}
|
|
161
|
-
function testOptionalSubfield(tag, subfieldCode) {
|
|
162
|
-
const subfields1 = field1.subfields.filter((subfield) => subfield.code === subfieldCode && valueCarriesMeaning(field1.tag, subfield.code, subfield.value));
|
|
163
|
-
const subfields2 = field2.subfields.filter((subfield) => subfield.code === subfieldCode && valueCarriesMeaning(field2.tag, subfield.code, subfield.value));
|
|
164
|
-
if (subfields1.length === 0 || subfields2.length === 0) {
|
|
165
|
-
return true;
|
|
166
|
-
}
|
|
167
|
-
const subfieldValues1 = subfields1.map((sf) => counterpartExtraNormalize(tag, subfieldCode, sf.value));
|
|
168
|
-
const subfieldValues2 = subfields2.map((sf) => counterpartExtraNormalize(tag, subfieldCode, sf.value));
|
|
169
|
-
if (subfieldValues1.every((val) => subfieldValues2.includes(val)) || subfieldValues2.every((val) => subfieldValues1.includes(val))) {
|
|
170
|
-
return true;
|
|
171
|
-
}
|
|
172
|
-
if (subfieldValues1.length === 1 && subfieldValues2.length === 1) {
|
|
173
|
-
return pairableValue(field1.tag, subfieldCode, subfieldValues1[0], subfieldValues2[0]) !== void 0;
|
|
174
|
-
}
|
|
175
|
-
return false;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
function mandatorySubfieldComparison(originalField1, originalField2, keySubfieldsAsString) {
|
|
179
|
-
const field1 = cloneAndNormalizeFieldForComparison(originalField1);
|
|
180
|
-
const field2 = cloneAndNormalizeFieldForComparison(originalField2);
|
|
181
|
-
if (keySubfieldsAsString === null) {
|
|
182
|
-
return fieldToString(field1) === fieldToString(field2);
|
|
183
|
-
}
|
|
184
|
-
const subfieldArray = keySubfieldsAsString.split("");
|
|
185
|
-
return subfieldArray.every((subfieldCode) => mandatorySingleSubfieldComparison(subfieldCode));
|
|
186
|
-
function mandatorySingleSubfieldComparison(subfieldCode) {
|
|
187
|
-
const subfieldValues1 = field1.subfields.filter((subfield) => subfield.code === subfieldCode).map((sf) => sf.value);
|
|
188
|
-
const subfieldValues2 = field2.subfields.filter((subfield) => subfield.code === subfieldCode).map((sf) => sf.value);
|
|
189
|
-
if (subfieldValues1.length !== subfieldValues2.length) {
|
|
190
|
-
debugDev(`mSC: Unique key: subfield ${subfieldCode} issues...`);
|
|
191
|
-
return false;
|
|
192
|
-
}
|
|
193
|
-
return subfieldValues1.every((value) => subfieldValues2.includes(value));
|
|
194
|
-
}
|
|
134
|
+
function hasCommonNominator(field1, field2, subfieldCode) {
|
|
135
|
+
const subfields1 = field1.subfields.filter((subfield) => subfield.code === subfieldCode && valueCarriesMeaning(field1.tag, subfield.code, subfield.value));
|
|
136
|
+
const subfields2 = field2.subfields.filter((subfield) => subfield.code === subfieldCode && valueCarriesMeaning(field2.tag, subfield.code, subfield.value));
|
|
137
|
+
return subfields1.length > 0 && subfields2.length > 0;
|
|
195
138
|
}
|
|
196
139
|
function tagToRegexp(tag, internalMerge = false) {
|
|
197
140
|
if (internalMerge && tag in counterpartRegexpsSingle) {
|
|
@@ -204,7 +147,7 @@ function tagToRegexp(tag, internalMerge = false) {
|
|
|
204
147
|
return new RegExp(`^${tag}$`, "u");
|
|
205
148
|
}
|
|
206
149
|
function areRequiredSubfieldsPresent(field) {
|
|
207
|
-
const subfieldString = getMergeConstraintsForTag(field.tag, "required");
|
|
150
|
+
const subfieldString = getMergeConstraintsForTag(field.tag, "required").join("");
|
|
208
151
|
if (subfieldString === null) {
|
|
209
152
|
return true;
|
|
210
153
|
}
|
|
@@ -218,15 +161,22 @@ function areRequiredSubfieldsPresent(field) {
|
|
|
218
161
|
return true;
|
|
219
162
|
});
|
|
220
163
|
}
|
|
221
|
-
function
|
|
222
|
-
const
|
|
223
|
-
if (
|
|
224
|
-
return
|
|
164
|
+
function getUnbalancedPairedSubfieldCode(field1, field2) {
|
|
165
|
+
const fullSubfieldString = getMergeConstraintsForTag(field1.tag, "paired").join("") || "";
|
|
166
|
+
if (fullSubfieldString === "") {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
const pairable = pairableIdentifier(field1, field2, "(FIN11)");
|
|
170
|
+
const subfieldString = pairable ? removeNameRelatedSubfieldCodes(fullSubfieldString, field1.tag) : fullSubfieldString;
|
|
171
|
+
debug(`CHECK ${pairable ? "PAIRABLE " : ""}${field1.tag} PAIRS: '${fullSubfieldString}' => '${subfieldString}'`);
|
|
172
|
+
if (subfieldString === "") {
|
|
173
|
+
return false;
|
|
225
174
|
}
|
|
226
175
|
const subfieldArray = subfieldString.split("");
|
|
227
|
-
return subfieldArray.
|
|
176
|
+
return subfieldArray.find((sfcode) => fieldHasNSubfields(field1, sfcode) !== fieldHasNSubfields(field2, sfcode));
|
|
228
177
|
}
|
|
229
178
|
function syntacticallyMergablePair(baseField, sourceField, config) {
|
|
179
|
+
nvdebug("CHECK SYNTAX");
|
|
230
180
|
if (!mergableIndicator1(baseField, sourceField, config)) {
|
|
231
181
|
nvdebug(`non-mergable (reason: indicator1): ${JSON.stringify(config)}`, debugDev);
|
|
232
182
|
return false;
|
|
@@ -247,14 +197,16 @@ function syntacticallyMergablePair(baseField, sourceField, config) {
|
|
|
247
197
|
nvdebug("non-mergable (reason: missing subfields)", debugDev);
|
|
248
198
|
return false;
|
|
249
199
|
}
|
|
250
|
-
|
|
251
|
-
|
|
200
|
+
const subfieldCodeThatFailsToPair = getUnbalancedPairedSubfieldCode(baseField, sourceField);
|
|
201
|
+
if (subfieldCodeThatFailsToPair) {
|
|
202
|
+
nvdebug(`non-mergable (reason: required subfield pair check failed for code: '${subfieldCodeThatFailsToPair}')`, debugDev);
|
|
252
203
|
return false;
|
|
253
204
|
}
|
|
254
205
|
return true;
|
|
255
206
|
}
|
|
256
207
|
function mergablePair(baseField, sourceField, config) {
|
|
257
208
|
if (!syntacticallyMergablePair(baseField, sourceField, config)) {
|
|
209
|
+
nvdebug("non-mergable (reason: syntax)", debugDev);
|
|
258
210
|
return false;
|
|
259
211
|
}
|
|
260
212
|
if (!semanticallyMergablePair(baseField, sourceField)) {
|
|
@@ -266,21 +218,41 @@ function mergablePair(baseField, sourceField, config) {
|
|
|
266
218
|
S: ${fieldToString(sourceField)}`, debugDev);
|
|
267
219
|
return true;
|
|
268
220
|
}
|
|
269
|
-
function
|
|
270
|
-
const
|
|
271
|
-
|
|
272
|
-
|
|
221
|
+
function removeNameRelatedSubfieldCodes(codestring, tag) {
|
|
222
|
+
const removables = getNameRelatedSubfieldCodes(tag);
|
|
223
|
+
return removeCharsFromString(codestring, removables);
|
|
224
|
+
function removeCharsFromString(string, removableCharsAsString) {
|
|
225
|
+
const removableChars = removableCharsAsString.split("");
|
|
226
|
+
return string.split("").filter((c) => !removableChars.includes(c)).join("");
|
|
227
|
+
}
|
|
228
|
+
function getNameRelatedSubfieldCodes(tag2) {
|
|
229
|
+
if (["100", "600", "700", "800"].includes(tag2)) {
|
|
230
|
+
return "abcdq";
|
|
231
|
+
}
|
|
232
|
+
if (["110", "610", "710", "810"].includes(tag2)) {
|
|
233
|
+
return "abcdn";
|
|
234
|
+
}
|
|
235
|
+
if (["111", "611", "711", "811"].includes(tag2)) {
|
|
236
|
+
return "acden";
|
|
237
|
+
}
|
|
238
|
+
return "";
|
|
273
239
|
}
|
|
274
|
-
|
|
275
|
-
|
|
240
|
+
}
|
|
241
|
+
function pairableIdentifier(field1, field2, prefix) {
|
|
242
|
+
const normalizedPrefix = prefix;
|
|
243
|
+
nvdebug(`PREF '${prefix}' => '${normalizedPrefix}'`);
|
|
244
|
+
const prefixLength = normalizedPrefix.length;
|
|
245
|
+
const identifiers1 = getIdentifiers(field1);
|
|
246
|
+
if (identifiers1.length !== 1) {
|
|
276
247
|
return false;
|
|
277
248
|
}
|
|
278
|
-
|
|
249
|
+
const identifiers2 = getIdentifiers(field2);
|
|
250
|
+
if (identifiers2.length !== 1) {
|
|
279
251
|
return false;
|
|
280
252
|
}
|
|
281
|
-
return
|
|
282
|
-
function
|
|
283
|
-
return field.subfields.filter((sf) => sf.code === "0").map((sf) => normalizeControlSubfieldValue(sf.value)).filter((val) => val.substring(0,
|
|
253
|
+
return identifiers1[0] === identifiers2[0];
|
|
254
|
+
function getIdentifiers(field) {
|
|
255
|
+
return field.subfields.filter((sf) => sf.code === "0").map((sf) => normalizeControlSubfieldValue(sf.value)).filter((val) => val.substring(0, prefixLength) === normalizedPrefix);
|
|
284
256
|
}
|
|
285
257
|
}
|
|
286
258
|
function hasRepeatableSubfieldThatShouldBeTreatedAsNonRepeatable(field) {
|
|
@@ -295,84 +267,110 @@ function hasRepeatableSubfieldThatShouldBeTreatedAsNonRepeatable(field) {
|
|
|
295
267
|
}
|
|
296
268
|
return false;
|
|
297
269
|
}
|
|
298
|
-
function
|
|
299
|
-
const
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
if (
|
|
270
|
+
function getRelevantSubfieldValues(field, subfieldCode) {
|
|
271
|
+
const values = field.subfields.filter((sf) => sf.code === subfieldCode).map((sf) => counterpartExtraNormalize(field.tag, subfieldCode, sf.value));
|
|
272
|
+
return values.filter((v) => valueCarriesMeaning(field.tag, subfieldCode, v));
|
|
273
|
+
}
|
|
274
|
+
function pairableValue(tag, subfieldCode, value1, value2) {
|
|
275
|
+
if (value1 === value2) {
|
|
276
|
+
return true;
|
|
277
|
+
}
|
|
278
|
+
if (withAndWithoutQualifierAgree(value1, value2, tag, subfieldCode)) {
|
|
279
|
+
return true;
|
|
280
|
+
}
|
|
281
|
+
if (partsAgree(value1, value2, tag, subfieldCode) || corporateNamesAgree(value1, value2, tag, subfieldCode)) {
|
|
304
282
|
return true;
|
|
305
283
|
}
|
|
306
|
-
|
|
284
|
+
return false;
|
|
285
|
+
}
|
|
286
|
+
function pairableValueInArray(tag, subfieldCode, val, arr) {
|
|
287
|
+
return arr.some((val2) => pairableValue(tag, subfieldCode, val, val2));
|
|
288
|
+
}
|
|
289
|
+
function tightSubfieldMatch(field1, field2, subfieldCode, mustHave = false) {
|
|
290
|
+
nvdebug(`${subfieldCode} F1: ${fieldToString(field1)}`);
|
|
291
|
+
nvdebug(`${subfieldCode} F2: ${fieldToString(field2)}`);
|
|
292
|
+
const values1 = getRelevantSubfieldValues(field1, subfieldCode);
|
|
293
|
+
const values2 = getRelevantSubfieldValues(field2, subfieldCode);
|
|
294
|
+
if (!mustHave) {
|
|
295
|
+
if (values1.length === 0 || values2.length === 0) {
|
|
296
|
+
return true;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
if (values1.length !== values2.length) {
|
|
307
300
|
return false;
|
|
308
301
|
}
|
|
309
|
-
|
|
310
|
-
|
|
302
|
+
nvdebug(`Compare $${subfieldCode} contents:
|
|
303
|
+
'${values1.join("'\n '")}' vs
|
|
304
|
+
'${values2.join("'\n '")}'`);
|
|
305
|
+
return values1.every((v) => pairableValueInArray(field1.tag, subfieldCode, v, values2)) && values2.every((v) => pairableValueInArray(field1.tag, subfieldCode, v, values1));
|
|
306
|
+
}
|
|
307
|
+
function looseSubfieldMatch(field1, field2, subfieldCode) {
|
|
308
|
+
const values1 = getRelevantSubfieldValues(field1, subfieldCode);
|
|
309
|
+
const values2 = getRelevantSubfieldValues(field2, subfieldCode);
|
|
310
|
+
if (values1.length === 0 || values2.length === 0) {
|
|
311
|
+
return true;
|
|
312
|
+
}
|
|
313
|
+
if (values1.every((v) => pairableValueInArray(field1.tag, subfieldCode, v, values2))) {
|
|
311
314
|
return true;
|
|
312
315
|
}
|
|
313
|
-
if (
|
|
316
|
+
if (values2.every((v) => pairableValueInArray(field1.tag, subfieldCode, v, values1))) {
|
|
314
317
|
return true;
|
|
315
318
|
}
|
|
316
|
-
nvdebug(` name mismatch:`, debugDev);
|
|
317
|
-
nvdebug(` '${fieldToString(reducedField1)}' vs`, debugDev);
|
|
318
|
-
nvdebug(` '${fieldToString(reducedField2)}'`, debugDev);
|
|
319
319
|
return false;
|
|
320
320
|
}
|
|
321
321
|
function semanticallyMergablePair(baseField, sourceField) {
|
|
322
|
-
|
|
323
|
-
|
|
322
|
+
const field1 = cloneAndNormalizeFieldForComparison(baseField);
|
|
323
|
+
const field2 = cloneAndNormalizeFieldForComparison(sourceField);
|
|
324
|
+
const string1 = fieldToString(field1);
|
|
325
|
+
const string2 = fieldToString(field2);
|
|
326
|
+
nvdebug(`IN ${baseField.tag}: pairableName():
|
|
327
|
+
'${string1}' vs
|
|
328
|
+
'${string2}'`, debugDev);
|
|
329
|
+
if (string1 === string2) {
|
|
330
|
+
return true;
|
|
331
|
+
}
|
|
332
|
+
const mergeConstraints = getMergeConstraintsForTag(field1.tag);
|
|
333
|
+
if (mergeConstraints.length === 0) {
|
|
324
334
|
return false;
|
|
325
335
|
}
|
|
326
|
-
if (
|
|
327
|
-
nvdebug(
|
|
336
|
+
if (hasRepeatableSubfieldThatShouldBeTreatedAsNonRepeatable(field1) || hasRepeatableSubfieldThatShouldBeTreatedAsNonRepeatable(field2)) {
|
|
337
|
+
nvdebug(`Unmergable: data is too complex to be automatically safely merged`, debugDev);
|
|
328
338
|
return false;
|
|
329
339
|
}
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
if (
|
|
334
|
-
return
|
|
340
|
+
const asteriMatch = pairableIdentifier(field1, field2, "(FIN11)");
|
|
341
|
+
const allRequired = mergeConstraints[0].required || "";
|
|
342
|
+
const reallyRequired = asteriMatch ? removeNameRelatedSubfieldCodes(allRequired, field1.tag) : allRequired;
|
|
343
|
+
if (!reallyRequired.split("").every((c) => tightSubfieldMatch(field1, field2, c, true))) {
|
|
344
|
+
return false;
|
|
335
345
|
}
|
|
336
|
-
const
|
|
337
|
-
const
|
|
338
|
-
if (
|
|
339
|
-
return
|
|
346
|
+
const allPaired = mergeConstraints[0].paired || "";
|
|
347
|
+
const reallyPaired = asteriMatch ? removeNameRelatedSubfieldCodes(allPaired, field1.tag) : allPaired;
|
|
348
|
+
if (!reallyPaired.split("").every((c) => tightSubfieldMatch(field1, field2, c, false))) {
|
|
349
|
+
return false;
|
|
340
350
|
}
|
|
341
|
-
|
|
342
|
-
|
|
351
|
+
const allKeys = mergeConstraints[0].key || "";
|
|
352
|
+
const relevantKeys = asteriMatch ? removeNameRelatedSubfieldCodes(allKeys, field1.tag) : allKeys;
|
|
353
|
+
if (!relevantKeys.split("").every((c) => looseSubfieldMatch(field1, field2, c))) {
|
|
354
|
+
return false;
|
|
343
355
|
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
function fieldToNamePart(field) {
|
|
347
|
-
const index = namePartThreshold(field);
|
|
348
|
-
const relevantSubfields = field.subfields.filter((sf, i) => i < index || index === -1).filter((sf) => !irrelevantSubfieldsInNameAndTitlePartComparison.includes(sf.code));
|
|
349
|
-
const subsetField = { "tag": field.tag, "ind1": field.ind1, "ind2": field.ind2, subfields: relevantSubfields };
|
|
350
|
-
return subsetField;
|
|
351
|
-
}
|
|
352
|
-
function fieldToTitlePart(field) {
|
|
353
|
-
const index = field.subfields.findIndex((currSubfield) => currSubfield.code === "t");
|
|
354
|
-
const relevantSubfields = field.subfields.filter((sf, i) => i >= index).filter((sf) => !irrelevantSubfieldsInNameAndTitlePartComparison.includes(sf.code));
|
|
355
|
-
const subsetField = { "tag": field.tag, "ind1": field.ind1, "ind2": field.ind2, subfields: relevantSubfields };
|
|
356
|
-
debugDev(`Title subset: ${fieldToString(subsetField)}`);
|
|
357
|
-
return subsetField;
|
|
358
|
-
}
|
|
359
|
-
function containsTitlePart(field) {
|
|
360
|
-
return fieldCanHaveTitlePart(field) && fieldHasSubfield(field, "t");
|
|
361
|
-
function fieldCanHaveTitlePart(field2) {
|
|
362
|
-
return ["100", "110", "111", "700", "710", "711"].includes(field2.tag);
|
|
356
|
+
if (allRequired.length > 0) {
|
|
357
|
+
return true;
|
|
363
358
|
}
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
if (!containsTitlePart(field1)) {
|
|
367
|
-
return !containsTitlePart(field2);
|
|
359
|
+
if (reallyPaired.length > 0 && field1.subfields.some((sf) => reallyPaired.includes(sf.code))) {
|
|
360
|
+
return true;
|
|
368
361
|
}
|
|
369
|
-
if (!
|
|
370
|
-
return
|
|
362
|
+
if (!tagIsRepeatable(field1.tag) || relevantKeys.length == 0) {
|
|
363
|
+
return true;
|
|
364
|
+
}
|
|
365
|
+
if (relevantKeys.length > 0) {
|
|
366
|
+
if (field1.subfields.some((sf) => relevantKeys.includes(sf.code)) || field2.subfields.some((sf) => relevantKeys.includes(sf.code))) {
|
|
367
|
+
return relevantKeys.split("").some((code) => hasCommonNominator(field1, field2, code));
|
|
368
|
+
}
|
|
371
369
|
}
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
return
|
|
370
|
+
nvdebug(` name mismatch (${keys}):`, debugDev);
|
|
371
|
+
nvdebug(` '${fieldToString(baseField)}' vs`, debugDev);
|
|
372
|
+
nvdebug(` '${fieldToString(sourceField)}'`, debugDev);
|
|
373
|
+
return false;
|
|
376
374
|
}
|
|
377
375
|
function getAlternativeNamesFrom9XX(record, field) {
|
|
378
376
|
if (!field.tag.match(/^(?:100|110|111|600|610|611|700|710|711)$/u)) {
|
|
@@ -418,7 +416,6 @@ function getCounterpartIndex(field, counterpartCands, altNames, config) {
|
|
|
418
416
|
return normalizedCounterpartCands.findIndex((normCandField) => altNames.some((altName) => mergablePairWithAltName(normCandField, normalizedField, altName, config)));
|
|
419
417
|
}
|
|
420
418
|
function field264Exception(baseField, sourceRecord, sourceField, config) {
|
|
421
|
-
nvdebug("Field 264 exception as per MET-456", debugDev);
|
|
422
419
|
if (baseField.tag !== "264") {
|
|
423
420
|
return false;
|
|
424
421
|
}
|
|
@@ -459,15 +456,8 @@ export function getCounterpart(baseRecord, sourceRecord, field, config) {
|
|
|
459
456
|
}
|
|
460
457
|
nvdebug(`Compare incoming '${fieldToString(field)}' with (up to) ${counterpartCands.length} existing field(s)`, debugDev);
|
|
461
458
|
const normalizedField = cloneAndNormalizeFieldForComparison(field);
|
|
462
|
-
nvdebug(`
|
|
459
|
+
nvdebug(` Normalize incoming field to: '${fieldToString(normalizedField)}'`, debugDev);
|
|
463
460
|
const uniqueAlternativeNames = getUniqueAlernativeNames();
|
|
464
|
-
function getUniqueAlernativeNames() {
|
|
465
|
-
if (baseIsSource(baseRecord, sourceRecord)) {
|
|
466
|
-
return [];
|
|
467
|
-
}
|
|
468
|
-
const alternativeNames = getAlternativeNamesFrom9XX(baseRecord, field).concat(getAlternativeNamesFrom9XX(sourceRecord, field));
|
|
469
|
-
return alternativeNames.filter((name, i) => alternativeNames.indexOf(name) === i);
|
|
470
|
-
}
|
|
471
461
|
const index = getCounterpartIndex(normalizedField, counterpartCands, uniqueAlternativeNames, config);
|
|
472
462
|
if (index > -1) {
|
|
473
463
|
return counterpartCands[index];
|
|
@@ -476,5 +466,12 @@ export function getCounterpart(baseRecord, sourceRecord, field, config) {
|
|
|
476
466
|
return counterpartCands[0];
|
|
477
467
|
}
|
|
478
468
|
return null;
|
|
469
|
+
function getUniqueAlernativeNames() {
|
|
470
|
+
if (baseIsSource(baseRecord, sourceRecord)) {
|
|
471
|
+
return [];
|
|
472
|
+
}
|
|
473
|
+
const alternativeNames = getAlternativeNamesFrom9XX(baseRecord, field).concat(getAlternativeNamesFrom9XX(sourceRecord, field));
|
|
474
|
+
return alternativeNames.filter((name, i) => alternativeNames.indexOf(name) === i);
|
|
475
|
+
}
|
|
479
476
|
}
|
|
480
477
|
//# sourceMappingURL=counterpartField.js.map
|