src/merge-fields/mergeConstraints.js
import createDebugLogger from 'debug';
const debug = createDebugLogger('@natlibfi/melinda-marc-record-merge-reducers:mergeConstraints');
//const debugData = debug.extend('data');
const debugDev = debug.extend('dev');
// Specs: https://workgroups.helsinki.fi/x/K1ohCw (though we occasionally differ from them)...
// "key" is an unique key that must match (be absent or exist+be identical) in both.
// "paired" refers to a field that must either exist in both or be absent in both (negative XOR). Typically it's not defined.
// NB: key+paired with identical values is an attempt to prevent copy for (ET) fields, and to force separate fields on (T) fields.
// NB! If base has eg. no 264, two+ 264 fields can be copied from the source.
// NB! not all X00 fields have, say, $x subfield. However, we can still share them...
// $h is non-1XX?, $i is 7XX only, $w is 8XX only...
const keyX00 = 'abcjloqrtuwx'; // Shared: $abcdefg...
const keyX10 = 'abcdfghlnoprstuwx';
const keyX11 = 'acdefghlnpqstuwx';
const keyX30 = 'adfghklmnoprstvwxyz';
const mergeConstraints = [
{'tag': '010', 'required': 'a', 'key': 'a'},
{'tag': '013', 'required': 'a', 'key': 'a'}, // We have 2 instances in Melinda...
{'tag': '015', 'required': 'a', 'key': 'a'},
{'tag': '016', 'required': 'a', 'key': 'a2'},
{'tag': '017', 'required': 'a', 'key': 'a'},
{'tag': '018', 'required': 'a', 'key': 'a'},
{'tag': '020', 'required': '', 'paired': 'a', 'key': 'a'}, // NB! how to handle $z-only cases? 'required-fallback'='z'?
{'tag': '022', 'required': '', 'paired': 'a', 'key': 'alz'},
{'tag': '024', 'required': '', 'paired': 'a', 'key': 'ad'},
{'tag': '025', 'required': 'a', 'key': 'a'},
{'tag': '026', 'required': 'a', 'key': 'a'},
{'tag': '027', 'required': 'a', 'key': 'a'}, // on tuolla pari $z:ää
{'tag': '028', 'required': 'a', 'key': 'ab'},
{'tag': '030', 'required': 'a', 'key': 'a'},
{'tag': '031', 'required': '', 'key': 'abcegmnopr2'}, // mites tämmöisen käytännössä avaimettoman klaarais? TODO: tests
{'tag': '032', 'required': 'a', 'key': 'ab'},
{'tag': '033', 'required': 'a', 'key': 'abcp0123'}, // 0,1% are without $a. Ignore them for now.
{'tag': '034', 'required': 'ab', 'key': 'abcdefghjkmnprstxyz0123'},
{'tag': '035', 'required': '', 'key': 'az'},
{'tag': '036', 'required': 'a', 'key': 'a'},
{'tag': '037', 'required': 'b', 'key': 'ab'},
{'tag': '039', 'required': 'a'},
{'tag': '040', 'required': '', 'key': ''},
{'tag': '041', 'required': '', 'paired': '2', 'key': ''}, // Don't put $2 in 'key'! hasCommonNominator() would get into trouble with it...
{'tag': '042', 'required': 'a', 'key': ''}, // NB: preprocessor hacks applied
{'tag': '043', 'required': 'a', 'key': 'abc'},
{'tag': '044', 'required': '', 'key': 'abc', 'paired': 'abc'},
{'tag': '045', 'required': '', 'key': 'abc', 'paired': 'abc'}, // (ET) // 045 is problematic either-$a or $b or $c...
{'tag': '046', 'required': 'a', 'key': 'abcdejklmnop', 'paired': 'abcdejklmnop'},
{'tag': '047', 'required': 'a', 'key': 'a2'},
{'tag': '048', 'required': '', 'paired': 'ab', 'key': 'ba'},
{'tag': '049', 'required': '', 'key': 'abcd'},
{'tag': '050', 'required': 'a', 'key': 'ab13'},
{'tag': '051', 'required': 'a', 'key': 'abc'}, // 2021-08-27: only one field in the whole Melinda
{'tag': '052', 'required': 'a', 'key': 'abd'},
{'tag': '055', 'required': 'a', 'key': 'ab'},
{'tag': '060', 'required': 'a', 'key': 'ab'},
{'tag': '061', 'required': 'a', 'paired': 'b', 'key': 'abc'},
{'tag': '066', 'required': 'c'},
{'tag': '070', 'required': 'a', 'key': 'ab'},
{'tag': '071', 'required': 'a', 'paired': 'abc', 'key': 'abc'}, // N=3
{'tag': '072', 'required': 'a', 'key': 'ax'},
{'tag': '074', 'required': '', 'paired': 'a', 'key': 'az'},
{'tag': '080', 'required': 'a', 'paired': 'bx', 'key': 'abx'},
{'tag': '082', 'required': 'a', 'paired': 'b', 'key': 'abmq2'},
{'tag': '083', 'required': 'a', 'paired': 'b', 'key': 'abmqy'},
{'tag': '084', 'required': 'a', 'paired': 'b2', 'key': 'abq2'},
{'tag': '085', 'required': '', 'paired': 'abcfrstuvwyz', 'key': 'abcfrstuvwxyz'},
{'tag': '086', 'required': '', 'paired': 'a', 'key': 'a'},
{'tag': '088', 'required': '', 'paired': 'a', 'key': 'a'},
// NB! 100, 110 and 111 may have title parts that are handled elsewhere
{'tag': '100', 'required': 'a', 'paired': 't', 'key': keyX00},
{'tag': '110', 'required': 'a', 'paired': 'bt', 'key': keyX10},
{'tag': '111', 'required': 'a', 'paired': 't', 'key': keyX11},
// NB! 130 has no name part, key is used for title part
{'tag': '130', 'required': 'a', 'key': keyX30},
{'tag': '210', 'required': 'a', 'key': 'ab'},
{'tag': '222', 'required': 'a', 'key': 'ab'},
{'tag': '240', 'required': 'a', 'key': 'adfghklmnoprs'},
{'tag': '242', 'required': 'a', 'key': 'abchnpy'},
{'tag': '243', 'required': 'a', 'key': 'adfghklmnoprs'},
{'tag': '245', 'required': 'a', 'key': 'abcghnps', 'paired': 'abnps'},
{'tag': '246', 'required': 'a', 'key': 'abfnp'},
{'tag': '247', 'required': 'a', 'key': 'abfnpx'},
{'tag': '250', 'required': 'a', 'key': 'ab'},
{'tag': '251', 'required': 'a', 'key': 'a'},
{'tag': '254', 'required': 'a', 'key': 'a'},
{'tag': '255', 'required': 'a', 'key': 'abcdefg', 'paired': 'abcdefg'},
{'tag': '256', 'required': 'a', 'key': 'a'},
{'tag': '257', 'required': 'a', 'key': 'a'},
{'tag': '258', 'required': 'a', 'key': 'a'}, // Melinda: N=1
//{'tag': '260', 'required': '', 'paired': 'abcefg', 'key': 'abcefg'},
{'tag': '260', 'required': '', 'key': 'abcefg'},
{'tag': '263', 'required': 'a', 'key': 'a'},
//{'tag': '264', 'required': '', 'paired': 'abc', 'key': 'abc'}, // NB "S.l." normalizations?" not implemented
{'tag': '264', 'required': '', 'key': 'abc'}, // NB "S.l." normalizations?" not implemented
// SKIP TAG 270 ON PURPOSE! Melinda's N=43.
{'tag': '300', 'required': 'a', 'key': 'abcefg'},
{'tag': '306', 'required': 'a', 'key': 'a'},
// SKIP TAG 307 ON PURPOSE! N=0
{'tag': '310', 'required': 'a', 'key': 'ab'},
{'tag': '321', 'required': 'a', 'key': 'ab'},
{'tag': '335', 'required': 'a', 'key': 'ab'}, // Melinda N=1 (a test field). M might increase?
{'tag': '336', 'required': 'b2', 'key': 'ab2'}, // MET-88: don't merge different $a subfields
{'tag': '337', 'required': 'b2', 'key': 'ab2'}, // MET-88: don't merge different $a subfields
{'tag': '338', 'required': 'b2', 'key': 'ab2'}, // / MET-88: don't merge different $a subfields
{'tag': '340', 'required': '', 'paired': 'abcdefghijkmnop', 'key': 'abcdefghijkmnop'},
{'tag': '341', 'required': '', 'paired': 'abcde', 'key': 'abcde'}, // NEW! Starting to appear!
{'tag': '342', 'required': '', 'paired': 'abcdefghijklmnopqrstuvw', 'key': 'abcdefghijklmnopqrstuvw'}, // SKIP 342. NOT SEEN!
{'tag': '343', 'required': '', 'paired': 'abcdefghi', 'key': 'abcdefghi'}, // SKIP 343.
{'tag': '344', 'required': '', 'paired': 'abcdefgh', 'key': 'abcdefgh'},
{'tag': '345', 'required': '', 'paired': 'abcd', 'key': 'abcd'},
{'tag': '346', 'required': '', 'paired': 'ab', 'key': 'ab'},
{'tag': '347', 'required': '', 'paired': 'abcdef', 'key': 'abcdef'},
{'tag': '348', 'required': '', 'paired': 'ab', 'key': 'ab'},
{'tag': '348', 'required': '', 'paired': 'abc', 'key': 'abc'},
{'tag': '351', 'required': '', 'paired': 'abc', 'key': 'abc'},
{'tag': '352', 'required': '', 'paired': 'abcdefgiq', 'key': 'abcdefgiq'},
{'tag': '355', 'required': '', 'paired': 'abcdefghj', 'key': 'abcdefghj'},
{'tag': '357', 'required': 'a', 'key': 'abcg'},
{'tag': '362', 'required': 'a', 'key': 'az'},
{'tag': '363', 'required': '', 'paired': 'abcdefghijklmuv', 'key': 'abcdefghijklmuv'},
{'tag': '365', 'required': 'b', 'paired': 'abcdefghijkm', 'key': 'abcdefghijkm'}, // N=0
{'tag': '366', 'required': '', 'paired': 'abcdefgjkm', 'key': 'abcdefgjkm'},
{'tag': '370', 'required': '', 'paired': 'cfgistuv', 'key': 'cfgistuv'},
{'tag': '377', 'required': '', 'paired': 'al', 'key': 'al'},
{'tag': '380', 'required': 'a', 'key': 'a'},
{'tag': '381', 'required': 'auv', 'key': 'auv'},
{'tag': '382', 'required': ''},
{'tag': '383', 'required': 'abcde', 'key': 'abcde'},
{'tag': '384', 'required': 'a', 'key': 'a'},
{'tag': '385', 'required': 'a', 'paired': 'abmn', 'key': 'abmn'},
{'tag': '386', 'required': 'a', 'paired': 'abmn', 'key': 'abmn'},
{'tag': '388', 'required': 'a', 'key': 'a'},
{'tag': '490', 'required': 'a', 'key': 'axvl'},
{'tag': '500', 'required': 'a', 'key': 'a'},
{'tag': '501', 'required': 'a', 'key': 'a'},
{'tag': '502', 'required': 'a', 'key': 'abcdgo'},
{'tag': '504', 'required': 'a', 'paired': 'ab', 'key': 'ab'},
{'tag': '505', 'required': '', 'paired': 'agrtu', 'key': 'agrtu'},
{'tag': '506', 'required': 'a', 'paired': '', 'key': 'abcdefgqu'},
{'tag': '507', 'required': 'a', 'paired': 'ab', 'key': 'ab'},
{'tag': '508', 'required': 'a', 'key': 'a'},
{'tag': '509', 'required': 'a', 'key': 'acd'},
{'tag': '510', 'required': 'a', 'key': 'abcx'},
{'tag': '511', 'required': 'a', 'key': 'a'},
{'tag': '513', 'required': '', 'paired': 'ab', 'key': 'ab'},
{'tag': '514', 'required': '', 'paired': 'abcdefghijkmuz', 'key': 'abcdefghijkmuz'},
{'tag': '515', 'required': 'a', 'key': 'a'},
{'tag': '518', 'required': '', 'paired': 'adop', 'key': 'adop'},
{'tag': '520', 'required': 'a', 'paired': 'abc', 'key': 'abc'},
{'tag': '521', 'required': 'a', 'paired': 'ab', 'key': 'ab'},
{'tag': '522', 'required': 'a', 'key': 'a'},
{'tag': '524', 'required': 'a', 'key': 'a'},
{'tag': '525', 'required': 'a', 'key': 'a'},
{'tag': '526', 'required': 'a', 'paired': 'abcdi', 'key': 'abcdi'},
{'tag': '530', 'required': 'a', 'paired': 'abcd', 'key': 'abcd'},
{'tag': '532', 'required': 'a', 'key': 'a'},
{'tag': '533', 'required': 'a', 'paired': 'abcdefmn7', 'key': 'abcdefmn7'},
{'tag': '534', 'required': '', 'paired': 'abcempt', 'key': 'abcempt'},
{'tag': '535', 'required': '', 'paired': 'abcdg', 'key': 'abcdg'},
{'tag': '536', 'required': '', 'paired': 'abcdefgh', 'key': 'abcdefgh'},
{'tag': '538', 'required': 'a', 'paired': 'aiu', 'key': 'aiu'},
{'tag': '540', 'required': '', 'paired': 'abcdfgqu', 'key': 'abcdfgqu'},
{'tag': '541', 'required': '', 'paired': 'abcdefhno', 'key': 'abcdefhno'},
{'tag': '542', 'required': '', 'paired': 'abcdfghijklmopqrsu', 'key': 'abcdfghijklmopqrsu'},
{'tag': '544', 'required': '', 'paired': 'abcden', 'key': 'abcden'},
{'tag': '545', 'required': '', 'paired': 'abu', 'key': 'abu'},
{'tag': '546', 'required': '', 'paired': 'ab', 'key': 'ab'},
{'tag': '547', 'required': 'a', 'key': 'a'},
{'tag': '550', 'required': 'a', 'key': 'a'},
{'tag': '552', 'required': '', 'paired': 'abcdefghijklmnopuz', 'key': 'abcdefghijklmnopuz'},
{'tag': '555', 'required': 'a', 'paired': 'abcdu', 'key': 'abcdu'},
{'tag': '556', 'required': 'a', 'key': 'az'},
{'tag': '561', 'required': 'a', 'key': 'au'},
{'tag': '562', 'required': '', 'paired': 'abcde', 'key': 'abcde'},
{'tag': '563', 'required': 'a', 'key': 'au'},
{'tag': '565', 'required': '', 'paired': 'abc', 'key': 'abc'},
{'tag': '567', 'required': '', 'paired': 'ab', 'key': 'ab'},
{'tag': '580', 'required': 'a', 'key': 'a'},
{'tag': '581', 'required': 'a', 'key': 'a'},
{'tag': '583', 'required': '', 'paired': 'abcdefhijklnou', 'key': 'abcdefhijklnou'},
{'tag': '584', 'required': '', 'paired': 'ab', 'key': 'ab'},
{'tag': '585', 'required': 'a', 'key': 'a'},
{'tag': '586', 'required': 'a', 'key': 'a'},
{'tag': '588', 'required': 'a', 'key': 'a'},
{'tag': '590', 'required': ''},
{'tag': '591', 'required': ''},
{'tag': '592', 'required': ''},
{'tag': '593', 'required': ''},
{'tag': '594', 'required': ''},
{'tag': '595', 'required': ''},
{'tag': '596', 'required': ''},
{'tag': '597', 'required': ''},
{'tag': '598', 'required': ''},
{'tag': '599', 'required': ''},
{'tag': '600', 'required': 'a', 'paired': 'tvxyz', 'key': keyX00},
{'tag': '610', 'required': 'a', 'paired': 'btvxyz', 'key': keyX10},
{'tag': '611', 'required': 'a', 'paired': 'tvxyz', 'key': keyX11},
{'tag': '630', 'required': 'a', 'paired': 'atvxyz', 'key': keyX30},
// NB! 700, 710 and 711 may have title parts that are handled elsewhere
{'tag': '647', 'required': 'a', 'paired': 'avxyz', 'key': 'acdgvxyz02'},
{'tag': '648', 'required': 'a', 'paired': 'avxyz', 'key': 'avxyz02'},
{'tag': '650', 'required': 'a', 'paired': 'abcdegvxyz', 'key': 'abcdegvxyz20'},
{'tag': '651', 'required': 'a', 'paired': 'aegvxyz', 'key': 'aegvxyz20'},
{'tag': '653', 'required': 'a', 'paired': 'a', 'key': 'a'}, // this is interesting as a can be repeated
{'tag': '654', 'required': '', 'paired': 'abcevxyz'},
{'tag': '655', 'required': 'a', 'paired': 'abcvxyz', 'key': 'avxyz20'},
{'tag': '656', 'required': 'a', 'paired': 'akvxyz'}, // N=0
{'tag': '657', 'required': 'a', 'paired': 'avxyz'}, // N=0
{'tag': '658', 'required': 'a', 'paired': 'abcd'}, // N=0
{'tag': '662', 'required': '', 'paired': 'abcdefgh'}, // N=0
{'tag': '688', 'required': 'a'}, // N=0
{'tag': '700', 'required': 'a', 'paired': 't', 'key': keyX00}, // h/i/m/o/r/s/x are missing from 100
{'tag': '710', 'required': 'a', 'paired': 'bt', 'key': keyX10}, // h/j/m/o/r/s/x are missing from 110
{'tag': '711', 'required': 'a', 'paired': 'cdeflns', 'key': keyX11}, // h/i/s/x are missing from 711
{'tag': '720', 'required': 'a', 'key': 'a'},
// NB! 730 has no name part, key is used for title part
{'tag': '730', 'required': 'a', 'key': keyX30}, // NB: 130->730 magic subfields might not agree...
{'tag': '740', 'required': 'a', 'key': 'ahnp'},
{'tag': '751', 'required': 'a', 'key': 'a'}, // N=11, kaikissa pelkkä $a
{'tag': '752', 'required': '', 'key': 'abcdefgh'}, // N=12234
{'tag': '753', 'required': '', 'key': 'abc'},
{'tag': '754', 'required': '', 'key': 'acdxz'}, // N=3
{'tag': '758', 'required': 'a', 'key': 'ai'}, // N=1
{'tag': '760', 'required': 'tw', key: 'twxy'},
{'tag': '762', 'required': 't', key: 'abcdhmstxy'},
{'tag': '765', 'required': 't', key: 'abcdhmrstuwxyz'},
{'tag': '767', 'required': 't', key: 'abcdhmrstuwxyz'},
{'tag': '770', 'required': 't', 'paired': 'ruxyz', key: 'abcdhmrstuwxyz'},
{'tag': '772', 'required': 't', key: 'abcdhmrstuwxyz'},
{'tag': '773', 'required': 'w', key: 'wgq'}, // Kirjavälitys should not have any component parts. However, this need to be re-thought...
// Currently we (appently) drop fields that don't contain 773$w...
{'tag': '774', 'required': '', 'paired': 'nruxyz', 'key': 'npqrstrxyz'},
{'tag': '775', 'required': '', 'paired': 'ruxyz', 'key': 'abcdefhmstuxyz'},
{'tag': '776', 'required': '', 'paired': 'ruxyz', 'key': 'abcdhmsuwxyz'},
{'tag': '777', 'required': '', 'paired': 'ruxyz', 'key': 'abcdhmstuxyz'},
{'tag': '780', 'required': '', 'paired': 'ruxyz', 'key': 'abcdhmstuxyz'},
{'tag': '785', 'required': '', 'paired': 'uxyz', 'key': 'abcdhmstuxyz'},
{'tag': '786', 'required': '', 'paired': 'abcrstuxyz', 'key': 'abcdhijmprstuxyz4'},
{'tag': '787', 'required': '', 'paired': 'abcdhmstuxyz4'},
{'tag': '788', 'required': '', 'paired': 'stx', 'key': 'abdestx'},
{'tag': '800', 'required': 'a', 'paired': 't', 'key': keyX00},
{'tag': '810', 'required': 'a', 'paired': 'bt', 'key': keyX10},
{'tag': '811', 'required': 'a', 'paired': 't', 'key': keyX11},
{'tag': '830', 'required': 'a', 'key': keyX30},
{'tag': '840', 'required': 'a'},
{'tag': '841', 'required': 'a'},
{'tag': '842', 'required': 'a'},
{'tag': '843', 'required': 'a'},
{'tag': '844', 'required': 'a'},
{'tag': '845', 'required': 'a'},
{'tag': '850', 'required': 'a', 'key': 'a'},
{'tag': '852', 'required': 'a'}, // HMM... we might want to reconsider this...
{'tag': '853', 'required': 'a'},
{'tag': '854', 'required': 'a'},
{'tag': '855', 'required': 'a'},
{'tag': '856', 'required': 'u', 'paired': 'u', 'key': 'opuw23'}, // 856 is built around $u...
{'tag': '863', 'required': 'a'},
{'tag': '864', 'required': 'a'},
{'tag': '865', 'required': 'a'},
{'tag': '866', 'required': 'a'},
{'tag': '867', 'required': 'a'},
{'tag': '868', 'required': 'a'},
{'tag': '876', 'required': 'a'},
{'tag': '877', 'required': 'a'},
{'tag': '878', 'required': 'a'},
{'tag': '880', 'required': '', 'paired': 'a', 'key': 'abcdefghijklmnopqrstuvwxyz'},
{'tag': '881', 'required': ''},
{'tag': '882', 'required': ''},
{'tag': '883', 'required': ''},
{'tag': '884', 'required': '', 'paired': 'agkq'},
{'tag': '885', 'required': ''},
{'tag': '886', 'required': ''},
{'tag': '887', 'required': ''},
{'tag': '900', 'required': ''},
{'tag': '901', 'required': ''},
{'tag': '910', 'required': ''},
{'tag': '935', 'required': 'a', 'key': 'az'}, // Fono information at least
{'tag': '940', 'required': ''},
{'tag': '946', 'required': 'a', 'key': 'abfnp'}, // Copied from 246. However, final version might contain some elements from field 245 as well
{'tag': '960', 'required': ''},
{'tag': '973', 'required': 'w', 'key': 'w'}, // Viola multi-hosts
{'tag': '995', 'required': ''},
{'tag': 'CAT', 'required': ''},
{'tag': 'LOW', 'required': ''},
{'tag': 'SID', 'required': ''}
];
function constraintToValue(tagsConstraints, constraintName) {
if (constraintName in tagsConstraints) {
return tagsConstraints[constraintName];
}
return null; // NB! "" might mean "apply to everything" (eg. 040.key) while null means that it is not applied.
}
export function getMergeConstraintsForTag(tag, constraintName) {
const tagsConstraintsArray = mergeConstraints.filter(entry => tag === entry.tag);
if (tagsConstraintsArray.length === 0) {
debugDev(`WARNING\tNo key found for ${tag}. Returning NULL!`);
return null;
}
// NB! should we support multiple contains for a field? Eg. 505$a vs 505($tg)+
if (tagsConstraintsArray.length > 1) { // eslint-disable-line functional/no-conditional-statements
debugDev(`WARNING\tMultiple values for '${constraintName}' (N=${tagsConstraintsArray.length}) found in ${tag}. Using first values.`);
}
return constraintToValue(tagsConstraintsArray[0], constraintName);
}