@openforis/arena-core 2.18.16 → 2.18.18

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.
@@ -1,10 +1,13 @@
1
1
  import { PointFactory } from '../pointFactory';
2
- const POINT_REGEX = /SRID=((EPSG:)?(\w+));POINT\((-?\d+(\.\d+)?) (-?\d+(\.\d+)?)\)/;
2
+ // coordinates can be in exponent notation (e.g. 1e-7), as generated by Number.toString or by PostgreSQL for very small or big numbers
3
+ const NUMBER_PATTERN = String.raw `-?\d+(?:\.\d+)?(?:[eE][-+]?\d+)?`;
4
+ const POINT_REGEX = new RegExp(String.raw `SRID=((EPSG:)?(\w+));POINT\((${NUMBER_PATTERN}) (${NUMBER_PATTERN})\)`);
3
5
  /**
4
6
  * Parses a point in the format: SRID=SRS_CODE;POINT(X Y)
5
7
  * (X and Y separated by a space). Valid examples are:
6
8
  * - SRID=EPSG:4326;POINT(12.489060 41.882788)
7
- * - SRID=4326;POINT(12 41).
9
+ * - SRID=4326;POINT(12 41)
10
+ * - SRID=4326;POINT(1e-7 41).
8
11
  *
9
12
  * @param {!(string | object)} value - The point to parse. It can be a string or an object.
10
13
  * @returns {Point} - The parsed Point object.
@@ -20,7 +23,7 @@ export const parse = (value) => {
20
23
  return null;
21
24
  const srs = match[3];
22
25
  const x = Number(match[4]);
23
- const y = Number(match[6]);
26
+ const y = Number(match[5]);
24
27
  return PointFactory.createInstance({ srs, x, y });
25
28
  }
26
29
  return null;
@@ -1 +1 @@
1
- {"version":3,"file":"parse.js","sourceRoot":"","sources":["../../../../src/geo/points/parse.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAE9C,MAAM,WAAW,GAAG,+DAA+D,CAAA;AAEnF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,KAAsB,EAAgB,EAAE;IAC5D,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAA;IAEvB,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAc,CAAA;IAEpD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACrC,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAA;QAEvB,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;QACpB,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;QAC1B,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;QAC1B,OAAO,YAAY,CAAC,cAAc,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAA;IACnD,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC,CAAA","sourcesContent":["import { Point } from '../point'\nimport { PointFactory } from '../pointFactory'\n\nconst POINT_REGEX = /SRID=((EPSG:)?(\\w+));POINT\\((-?\\d+(\\.\\d+)?) (-?\\d+(\\.\\d+)?)\\)/\n\n/**\n * Parses a point in the format: SRID=SRS_CODE;POINT(X Y)\n * (X and Y separated by a space). Valid examples are:\n * - SRID=EPSG:4326;POINT(12.489060 41.882788)\n * - SRID=4326;POINT(12 41).\n *\n * @param {!(string | object)} value - The point to parse. It can be a string or an object.\n * @returns {Point} - The parsed Point object.\n */\nexport const parse = (value: string | object): Point | null => {\n if (!value) return null\n\n if (typeof value === 'object') return value as Point\n\n if (typeof value === 'string') {\n const match = POINT_REGEX.exec(value)\n if (!match) return null\n\n const srs = match[3]\n const x = Number(match[4])\n const y = Number(match[6])\n return PointFactory.createInstance({ srs, x, y })\n }\n return null\n}\n"]}
1
+ {"version":3,"file":"parse.js","sourceRoot":"","sources":["../../../../src/geo/points/parse.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAE9C,sIAAsI;AACtI,MAAM,cAAc,GAAG,MAAM,CAAC,GAAG,CAAA,kCAAkC,CAAA;AACnE,MAAM,WAAW,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC,GAAG,CAAA,gCAAgC,cAAc,MAAM,cAAc,KAAK,CAAC,CAAA;AAEjH;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,KAAsB,EAAgB,EAAE;IAC5D,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAA;IAEvB,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAc,CAAA;IAEpD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACrC,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAA;QAEvB,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;QACpB,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;QAC1B,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;QAC1B,OAAO,YAAY,CAAC,cAAc,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAA;IACnD,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC,CAAA","sourcesContent":["import { Point } from '../point'\nimport { PointFactory } from '../pointFactory'\n\n// coordinates can be in exponent notation (e.g. 1e-7), as generated by Number.toString or by PostgreSQL for very small or big numbers\nconst NUMBER_PATTERN = String.raw`-?\\d+(?:\\.\\d+)?(?:[eE][-+]?\\d+)?`\nconst POINT_REGEX = new RegExp(String.raw`SRID=((EPSG:)?(\\w+));POINT\\((${NUMBER_PATTERN}) (${NUMBER_PATTERN})\\)`)\n\n/**\n * Parses a point in the format: SRID=SRS_CODE;POINT(X Y)\n * (X and Y separated by a space). Valid examples are:\n * - SRID=EPSG:4326;POINT(12.489060 41.882788)\n * - SRID=4326;POINT(12 41)\n * - SRID=4326;POINT(1e-7 41).\n *\n * @param {!(string | object)} value - The point to parse. It can be a string or an object.\n * @returns {Point} - The parsed Point object.\n */\nexport const parse = (value: string | object): Point | null => {\n if (!value) return null\n\n if (typeof value === 'object') return value as Point\n\n if (typeof value === 'string') {\n const match = POINT_REGEX.exec(value)\n if (!match) return null\n\n const srs = match[3]\n const x = Number(match[4])\n const y = Number(match[5])\n return PointFactory.createInstance({ srs, x, y })\n }\n return null\n}\n"]}
@@ -147,18 +147,26 @@ const extractCategoryItemCodeFromValue = (params) => {
147
147
  }
148
148
  return getValueCode(value);
149
149
  };
150
- const dateTimeComparator = (params) => (paramsComparator) => {
151
- const { formatsSource, formatTo } = params;
150
+ const dateFormats = {
151
+ formatsSource: [DateFormats.dateDisplay, DateFormats.dateStorage],
152
+ formatTo: DateFormats.dateStorage,
153
+ };
154
+ const timeFormats = {
155
+ formatsSource: [DateFormats.timeStorage, DateFormats.timeWithSeconds],
156
+ formatTo: DateFormats.timeWithSeconds,
157
+ };
158
+ const toDateTime = (params) => {
159
+ const { formatsSource, formatTo, value } = params;
160
+ if (value instanceof Date) {
161
+ return Dates.format(value, formatTo);
162
+ }
163
+ const formatFrom = formatsSource.find((format) => Dates.isValidDateInFormat(value, format));
164
+ return formatFrom ? Dates.convertDate({ dateStr: value, formatFrom, formatTo }) : null;
165
+ };
166
+ const dateTimeComparator = (formats) => (paramsComparator) => {
152
167
  const { value, valueSearch } = paramsComparator;
153
- const toDateTime = (val) => {
154
- if (val instanceof Date) {
155
- return Dates.format(val, formatTo);
156
- }
157
- const formatFrom = formatsSource.find((format) => Dates.isValidDateInFormat(val, format));
158
- return formatFrom ? Dates.convertDate({ dateStr: val, formatFrom, formatTo }) : null;
159
- };
160
- const dateTime = toDateTime(value);
161
- const dateTimeSearch = toDateTime(valueSearch);
168
+ const dateTime = toDateTime(Object.assign(Object.assign({}, formats), { value }));
169
+ const dateTimeSearch = toDateTime(Object.assign(Object.assign({}, formats), { value: valueSearch }));
162
170
  return !!dateTime && !!dateTimeSearch && dateTime === dateTimeSearch;
163
171
  };
164
172
  const valueComparatorByNodeDefType = {
@@ -179,10 +187,7 @@ const valueComparatorByNodeDefType = {
179
187
  const { value, valueSearch } = params;
180
188
  return Objects.isEqual(value, valueSearch);
181
189
  },
182
- [NodeDefType.date]: dateTimeComparator({
183
- formatsSource: [DateFormats.dateDisplay, DateFormats.dateStorage],
184
- formatTo: DateFormats.dateStorage,
185
- }),
190
+ [NodeDefType.date]: dateTimeComparator(dateFormats),
186
191
  [NodeDefType.decimal]: singlePropValueEqualComparator,
187
192
  [NodeDefType.integer]: singlePropValueEqualComparator,
188
193
  [NodeDefType.taxon]: ({ value, valueSearch }) => {
@@ -195,10 +200,7 @@ const valueComparatorByNodeDefType = {
195
200
  return value[ValuePropsTaxon.taxonUuid] === valueSearch[ValuePropsTaxon.taxonUuid];
196
201
  },
197
202
  [NodeDefType.text]: singlePropValueEqualComparator,
198
- [NodeDefType.time]: dateTimeComparator({
199
- formatsSource: [DateFormats.timeStorage, DateFormats.timeWithSeconds],
200
- formatTo: DateFormats.timeWithSeconds,
201
- }),
203
+ [NodeDefType.time]: dateTimeComparator(timeFormats),
202
204
  };
203
205
  const isValueEqual = (params) => {
204
206
  const { survey, nodeDef, value, valueSearch, record = undefined, parentNode = undefined, strict = false } = params;
@@ -212,6 +214,43 @@ const isValueEqual = (params) => {
212
214
  }
213
215
  return valueComparator({ survey, nodeDef, record, parentNode, value, valueSearch, strict });
214
216
  };
217
+ const primitiveTypes = new Set(['string', 'number', 'boolean', 'undefined', 'bigint']);
218
+ // token of a primitive value, equal for strictly equal (===) values only (distinguishes "1" from 1);
219
+ // undefined for non primitive values and NaN (not equal to itself)
220
+ const toPrimitiveToken = (value) => (value === null || primitiveTypes.has(typeof value)) && !Number.isNaN(value)
221
+ ? `${typeof value}:${String(value)}`
222
+ : undefined;
223
+ const dateTimeKeyExtractor = (formats) => ({ value }) => toDateTime(Object.assign(Object.assign({}, formats), { value })) || undefined;
224
+ const valueEqualityKeyByNodeDefType = {
225
+ [NodeDefType.boolean]: ({ value }) => String(value),
226
+ [NodeDefType.code]: ({ survey, value }) => {
227
+ const code = extractCategoryItemCodeFromValue({ survey, value });
228
+ return code ? toPrimitiveToken(code) : undefined;
229
+ },
230
+ [NodeDefType.date]: dateTimeKeyExtractor(dateFormats),
231
+ [NodeDefType.decimal]: ({ value }) => String(value),
232
+ [NodeDefType.integer]: ({ value }) => String(value),
233
+ [NodeDefType.taxon]: ({ value }) => toPrimitiveToken(value[ValuePropsTaxon.taxonUuid]),
234
+ [NodeDefType.text]: ({ value }) => String(value),
235
+ [NodeDefType.time]: dateTimeKeyExtractor(timeFormats),
236
+ };
237
+ /**
238
+ * Returns a key such that two values have the same key if and only if they are equal according to isValueEqual
239
+ * (not strict, without record), or undefined if such a key cannot be built for the value (e.g. NaN, empty objects, code values
240
+ * without code, invalid dates, coordinates): such values must be compared with isValueEqual.
241
+ * Not exported in NodeValues: internal, used to look up duplicate values without comparing every pair of values.
242
+ */
243
+ export const getValueEqualityKey = (params) => {
244
+ const { survey, nodeDef, value } = params;
245
+ if (Objects.isEmpty(value)) {
246
+ // empty values are equal only if identical (===)
247
+ const token = toPrimitiveToken(value);
248
+ return token === undefined ? undefined : `empty:${token}`;
249
+ }
250
+ const keyExtractor = valueEqualityKeyByNodeDefType[nodeDef.type];
251
+ const key = keyExtractor === null || keyExtractor === void 0 ? void 0 : keyExtractor({ survey, value });
252
+ return key === undefined ? undefined : `value:${key}`;
253
+ };
215
254
  const getValueAsPoint = (params) => {
216
255
  const { survey, node } = params;
217
256
  const nodeValue = node === null || node === void 0 ? void 0 : node.value;
@@ -1 +1 @@
1
- {"version":3,"file":"nodeValues.js","sourceRoot":"","sources":["../../../src/node/nodeValues.ts"],"names":[],"mappings":"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","sourcesContent":["import { CategoryItems } from '../category'\nimport { Point, PointFactory } from '../geo'\nimport { NodeDef, NodeDefCode, NodeDefProps, NodeDefType } from '../nodeDef'\nimport { ArenaRecord, Records } from '../record'\nimport { Survey, Surveys } from '../survey'\nimport { DateFormats, Dates, Objects } from '../utils'\nimport { ArenaRecordNode } from './node'\nimport { NodeValueCoordinate } from './nodeValue/coordinate'\nimport {\n ValuePropsCode,\n ValuePropsCoordinate,\n ValuePropsDate,\n ValuePropsFile,\n ValuePropsTaxon,\n ValuePropsTime,\n} from './nodeValueProps'\n\n/**\n * Props of node value indexed by node def type.\n * The node definitions here are only the ones of \"composite\" attributes.\n */\nexport const ValuePropsByType: { [key in NodeDefType]: { [key: string]: string } | null } = {\n [NodeDefType.boolean]: null,\n [NodeDefType.code]: ValuePropsCode,\n [NodeDefType.coordinate]: ValuePropsCoordinate,\n [NodeDefType.date]: ValuePropsDate,\n [NodeDefType.decimal]: null,\n [NodeDefType.geo]: null,\n [NodeDefType.entity]: null,\n [NodeDefType.file]: ValuePropsFile,\n [NodeDefType.integer]: null,\n [NodeDefType.taxon]: ValuePropsTaxon,\n [NodeDefType.time]: ValuePropsTime,\n [NodeDefType.text]: null,\n // layout elements\n [NodeDefType.formHeader]: null,\n}\n\nconst getValuePropRaw = (params: { value: any; prop: string; defaultValue?: any }): any => {\n const { value, prop, defaultValue } = params\n const valueProp = value?.[prop]\n return valueProp === undefined ? defaultValue : valueProp\n}\n\nconst getNodeValuePropRaw = (params: { node: ArenaRecordNode; prop: string; defaultValue?: any }): any => {\n const { node, prop, defaultValue } = params\n return getValuePropRaw({ value: node.value, prop, defaultValue })\n}\n\n// Code\nconst newCodeValue = (params: { itemUuid: string }) => {\n const { itemUuid } = params\n return { [ValuePropsCode[ValuePropsCode.itemUuid]]: itemUuid }\n}\n\nconst getItemUuid = (node: ArenaRecordNode): string | undefined =>\n getNodeValuePropRaw({ node, prop: ValuePropsCode[ValuePropsCode.itemUuid] })\n\nconst getValueCode = (value: any): string | undefined =>\n getValuePropRaw({ value, prop: ValuePropsCode[ValuePropsCode.code] })\n\nconst getValueItemUuid = (value: any): string | undefined =>\n getValuePropRaw({ value, prop: ValuePropsCode[ValuePropsCode.itemUuid] })\n\nconst getDateTimePart = (params: { node: ArenaRecordNode; index: number; separator: string }): number => {\n const { node, index, separator } = params\n return Number((node.value ?? '').split(separator)[index]?.trim())\n}\n\n// Date\nconst getDatePart =\n (index: number) =>\n (node: ArenaRecordNode): number =>\n getDateTimePart({ node, index, separator: '-' })\nconst getDateYear = getDatePart(0)\nconst getDateMonth = getDatePart(1)\nconst getDateDay = getDatePart(2)\n\nconst _datePropGetters: { [key in ValuePropsDate]: (node: ArenaRecordNode) => number } = {\n [ValuePropsDate.day]: getDateDay,\n [ValuePropsDate.month]: getDateMonth,\n [ValuePropsDate.year]: getDateYear,\n}\n\n// File\nconst getFileName = (node: ArenaRecordNode): string | undefined =>\n getNodeValuePropRaw({ node, prop: ValuePropsFile.fileName })\nconst getFileNameCalculated = (node: ArenaRecordNode): string | undefined =>\n getNodeValuePropRaw({ node, prop: ValuePropsFile.fileNameCalculated })\nconst getFileSize = (node: ArenaRecordNode): string | undefined =>\n getNodeValuePropRaw({ node, prop: ValuePropsFile.fileSize })\nconst getFileUuid = (node: ArenaRecordNode): string | undefined =>\n getNodeValuePropRaw({ node, prop: ValuePropsFile.fileUuid })\n\n// Taxon\nconst getTaxonUuid = (node: ArenaRecordNode): string | undefined =>\n getNodeValuePropRaw({ node, prop: ValuePropsTaxon[ValuePropsTaxon.taxonUuid] })\nconst getValueTaxonUuid = (value: any): string | undefined =>\n getValuePropRaw({ value, prop: ValuePropsTaxon[ValuePropsTaxon.taxonUuid] })\nconst getScientificName = (node: ArenaRecordNode): string | undefined =>\n getNodeValuePropRaw({ node, prop: ValuePropsTaxon[ValuePropsTaxon.scientificName] })\nconst getValueScientificName = (value: any): string | undefined =>\n getValuePropRaw({ value, prop: ValuePropsTaxon[ValuePropsTaxon.scientificName] })\nconst getVernacularName = (node: ArenaRecordNode): string | undefined =>\n getNodeValuePropRaw({ node, prop: ValuePropsTaxon[ValuePropsTaxon.vernacularName] })\nconst getValueVernacularName = (value: any): string | undefined =>\n getValuePropRaw({ value, prop: ValuePropsTaxon[ValuePropsTaxon.vernacularName] })\nconst getVernacularNameUuid = (node: ArenaRecordNode): string | undefined =>\n getNodeValuePropRaw({ node, prop: ValuePropsTaxon[ValuePropsTaxon.vernacularNameUuid] })\nconst getValueVernacularNameUuid = (value: any): string | undefined =>\n getValuePropRaw({ value, prop: ValuePropsTaxon[ValuePropsTaxon.vernacularNameUuid] })\n\n// Time\nconst _getTimePart =\n (index: number) =>\n (node: ArenaRecordNode): number =>\n getDateTimePart({ node, index, separator: ':' })\nconst getTimeHour = _getTimePart(0)\nconst getTimeMinute = _getTimePart(1)\nconst getTimeSeconds = (node: ArenaRecordNode): number => {\n const parts = (node.value ?? '').split(':')\n return parts.length > 2 ? _getTimePart(2)(node) : 0\n}\n\nconst _timePropGetters: { [key in ValuePropsTime]: any } = {\n [ValuePropsTime.hour]: getTimeHour,\n [ValuePropsTime.minute]: getTimeMinute,\n [ValuePropsTime.seconds]: getTimeSeconds,\n}\n\nconst _valuePropGetters: { [key in NodeDefType]?: (prop: string) => (node: ArenaRecordNode) => number } = {\n [NodeDefType.date]: (prop: string) => _datePropGetters[ValuePropsDate[prop as keyof typeof ValuePropsDate]],\n [NodeDefType.time]: (prop: string) => _timePropGetters[ValuePropsTime[prop as keyof typeof ValuePropsTime]],\n}\n\nconst isValueProp = (params: { nodeDef: NodeDef<NodeDefType, NodeDefProps>; prop: string }): boolean => {\n const { nodeDef, prop } = params\n return Object.values(ValuePropsByType[nodeDef.type] ?? {}).includes(prop)\n}\n\nconst getNodeValueProp = (params: {\n nodeDef: NodeDef<NodeDefType, NodeDefProps>\n node: ArenaRecordNode\n prop: string\n}): any => {\n const { nodeDef, node, prop } = params\n const propGetter: any = _valuePropGetters[nodeDef.type]\n return propGetter ? propGetter(prop)(node) : getNodeValuePropRaw({ node, prop })\n}\n\n// value compare\n\nconst singlePropValueEqualComparator = (params: NodeValuesCompareParams): boolean => {\n const { value, valueSearch } = params\n return value === valueSearch || String(value) === String(valueSearch)\n}\n\nconst extractCategoryItemUuidFromValue = (params: NodeValuesCompareParams): string | undefined => {\n const { survey, nodeDef, record, parentNode, value } = params\n const valueItemUuid = getValueItemUuid(value)\n if (valueItemUuid) {\n return valueItemUuid\n }\n // find itemUuid by code\n const valueCode = getValueCode(value)\n if (Objects.isEmpty(valueCode)) return undefined\n\n const nodeDefCode = nodeDef as NodeDefCode\n const code = valueCode!\n const categoryUuid = nodeDefCode.props?.categoryUuid\n const levelIndex = Surveys.getNodeDefCategoryLevelIndex({ survey, nodeDef: nodeDefCode })\n if (levelIndex === 0) {\n return Surveys.getCategoryItemUuidByCode({ survey, categoryUuid, parentItemUuid: undefined, code })\n }\n if (!record || !parentNode) return undefined\n\n const parentCodeAttribute = Records.getParentCodeAttribute(parentNode, nodeDef as NodeDefCode)(record)\n if (!parentCodeAttribute) return undefined\n\n const parentItemUuid = getItemUuid(parentCodeAttribute)\n if (!parentItemUuid) return undefined\n\n return Surveys.getCategoryItemUuidByCode({\n survey,\n categoryUuid,\n parentItemUuid,\n code,\n })\n}\n\nconst extractCategoryItemCodeFromValue = (params: { survey: Survey; value: any }): string | undefined => {\n const { survey, value } = params\n const itemUuid = getValueItemUuid(value)\n if (itemUuid) {\n const item = Surveys.getCategoryItemByUuid({ survey, itemUuid })\n return item ? CategoryItems.getCode(item) : undefined\n }\n return getValueCode(value)\n}\n\nconst dateTimeComparator =\n (params: { formatsSource: DateFormats[]; formatTo: DateFormats }) =>\n (paramsComparator: NodeValuesCompareParams): boolean => {\n const { formatsSource, formatTo } = params\n const { value, valueSearch } = paramsComparator\n const toDateTime = (val: any) => {\n if (val instanceof Date) {\n return Dates.format(val, formatTo)\n }\n const formatFrom = formatsSource.find((format) => Dates.isValidDateInFormat(val, format))\n return formatFrom ? Dates.convertDate({ dateStr: val, formatFrom, formatTo }) : null\n }\n const dateTime = toDateTime(value)\n const dateTimeSearch = toDateTime(valueSearch)\n return !!dateTime && !!dateTimeSearch && dateTime === dateTimeSearch\n }\n\nconst valueComparatorByNodeDefType: { [key in NodeDefType]?: (params: NodeValuesCompareParams) => boolean } = {\n [NodeDefType.boolean]: singlePropValueEqualComparator,\n [NodeDefType.code]: (params: NodeValuesCompareParams): boolean => {\n const { survey, record, value, valueSearch, strict } = params\n if (!strict || !record) {\n // compare just codes (record not available, tricky to find the \"correct\" category item without knowing its parent item)\n const code = extractCategoryItemCodeFromValue({ survey, value })\n const codeSearch = extractCategoryItemCodeFromValue({ survey, value: valueSearch })\n return !!code && !!codeSearch && code === codeSearch\n }\n const itemUuid = extractCategoryItemUuidFromValue(params)\n const itemUuidSearch = extractCategoryItemUuidFromValue({\n ...params,\n value: valueSearch,\n })\n return !!itemUuidSearch && !!itemUuid && itemUuidSearch === itemUuid\n },\n [NodeDefType.coordinate]: (params: NodeValuesCompareParams) => {\n const { value, valueSearch } = params\n return Objects.isEqual(value, valueSearch)\n },\n [NodeDefType.date]: dateTimeComparator({\n formatsSource: [DateFormats.dateDisplay, DateFormats.dateStorage],\n formatTo: DateFormats.dateStorage,\n }),\n [NodeDefType.decimal]: singlePropValueEqualComparator,\n [NodeDefType.integer]: singlePropValueEqualComparator,\n [NodeDefType.taxon]: ({ value, valueSearch }) => {\n if (value === valueSearch) return true\n if (!value) return false\n if (!valueSearch) return false\n return value[ValuePropsTaxon.taxonUuid] === valueSearch[ValuePropsTaxon.taxonUuid]\n },\n [NodeDefType.text]: singlePropValueEqualComparator,\n [NodeDefType.time]: dateTimeComparator({\n formatsSource: [DateFormats.timeStorage, DateFormats.timeWithSeconds],\n formatTo: DateFormats.timeWithSeconds,\n }),\n}\n\ninterface NodeValuesCompareParams {\n survey: Survey\n nodeDef: NodeDef<NodeDefType, any>\n value: any\n valueSearch: any\n record?: ArenaRecord\n parentNode?: ArenaRecordNode\n strict?: boolean\n}\n\nconst isValueEqual = (params: NodeValuesCompareParams): boolean => {\n const { survey, nodeDef, value, valueSearch, record = undefined, parentNode = undefined, strict = false } = params\n if (value === valueSearch) return true\n if (Objects.isEmpty(value) || Objects.isEmpty(valueSearch)) return false\n\n const valueComparator = valueComparatorByNodeDefType[nodeDef.type]\n if (!valueComparator) {\n return Objects.isEqual(value, valueSearch)\n }\n return valueComparator({ survey, nodeDef, record, parentNode, value, valueSearch, strict })\n}\n\nconst getValueAsPoint = (params: { survey: Survey; node: ArenaRecordNode }): Point | null => {\n const { survey, node } = params\n const nodeValue = node?.value\n\n if (!nodeValue) return null\n\n const nodeDef = Surveys.getNodeDefByUuid({ survey, uuid: node.nodeDefUuid })\n if (nodeDef.type === NodeDefType.coordinate) {\n const { x, y, srs } = nodeValue as NodeValueCoordinate\n\n const srsObject = Surveys.getSRSByCode(srs)(survey)\n if (!srsObject) return null\n\n return PointFactory.createInstance({ srs, x, y })\n }\n return null\n}\n\nexport const NodeValues = {\n ValuePropsByType,\n ValuePropsCode,\n ValuePropsCoordinate,\n ValuePropsDate,\n ValuePropsFile,\n ValuePropsTaxon,\n ValuePropsTime,\n\n getNodeValueProp,\n isValueProp,\n\n // coordinate\n getValueAsPoint,\n\n // code\n newCodeValue,\n getItemUuid,\n getValueCode,\n getValueItemUuid,\n\n // date\n getDateYear,\n getDateMonth,\n getDateDay,\n\n // file\n getFileName,\n getFileNameCalculated,\n getFileSize,\n getFileUuid,\n\n // taxon\n getTaxonUuid,\n getValueTaxonUuid,\n getScientificName,\n getValueScientificName,\n getVernacularName,\n getValueVernacularName,\n getVernacularNameUuid,\n getValueVernacularNameUuid,\n\n // time\n getTimeHour,\n getTimeMinute,\n getTimeSeconds,\n\n // utils\n isValueEqual,\n}\n"]}
1
+ {"version":3,"file":"nodeValues.js","sourceRoot":"","sources":["../../../src/node/nodeValues.ts"],"names":[],"mappings":"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","sourcesContent":["import { CategoryItems } from '../category'\nimport { Point, PointFactory } from '../geo'\nimport { NodeDef, NodeDefCode, NodeDefProps, NodeDefType } from '../nodeDef'\nimport { ArenaRecord, Records } from '../record'\nimport { Survey, Surveys } from '../survey'\nimport { DateFormats, Dates, Objects } from '../utils'\nimport { ArenaRecordNode } from './node'\nimport { NodeValueCoordinate } from './nodeValue/coordinate'\nimport {\n ValuePropsCode,\n ValuePropsCoordinate,\n ValuePropsDate,\n ValuePropsFile,\n ValuePropsTaxon,\n ValuePropsTime,\n} from './nodeValueProps'\n\n/**\n * Props of node value indexed by node def type.\n * The node definitions here are only the ones of \"composite\" attributes.\n */\nexport const ValuePropsByType: { [key in NodeDefType]: { [key: string]: string } | null } = {\n [NodeDefType.boolean]: null,\n [NodeDefType.code]: ValuePropsCode,\n [NodeDefType.coordinate]: ValuePropsCoordinate,\n [NodeDefType.date]: ValuePropsDate,\n [NodeDefType.decimal]: null,\n [NodeDefType.geo]: null,\n [NodeDefType.entity]: null,\n [NodeDefType.file]: ValuePropsFile,\n [NodeDefType.integer]: null,\n [NodeDefType.taxon]: ValuePropsTaxon,\n [NodeDefType.time]: ValuePropsTime,\n [NodeDefType.text]: null,\n // layout elements\n [NodeDefType.formHeader]: null,\n}\n\nconst getValuePropRaw = (params: { value: any; prop: string; defaultValue?: any }): any => {\n const { value, prop, defaultValue } = params\n const valueProp = value?.[prop]\n return valueProp === undefined ? defaultValue : valueProp\n}\n\nconst getNodeValuePropRaw = (params: { node: ArenaRecordNode; prop: string; defaultValue?: any }): any => {\n const { node, prop, defaultValue } = params\n return getValuePropRaw({ value: node.value, prop, defaultValue })\n}\n\n// Code\nconst newCodeValue = (params: { itemUuid: string }) => {\n const { itemUuid } = params\n return { [ValuePropsCode[ValuePropsCode.itemUuid]]: itemUuid }\n}\n\nconst getItemUuid = (node: ArenaRecordNode): string | undefined =>\n getNodeValuePropRaw({ node, prop: ValuePropsCode[ValuePropsCode.itemUuid] })\n\nconst getValueCode = (value: any): string | undefined =>\n getValuePropRaw({ value, prop: ValuePropsCode[ValuePropsCode.code] })\n\nconst getValueItemUuid = (value: any): string | undefined =>\n getValuePropRaw({ value, prop: ValuePropsCode[ValuePropsCode.itemUuid] })\n\nconst getDateTimePart = (params: { node: ArenaRecordNode; index: number; separator: string }): number => {\n const { node, index, separator } = params\n return Number((node.value ?? '').split(separator)[index]?.trim())\n}\n\n// Date\nconst getDatePart =\n (index: number) =>\n (node: ArenaRecordNode): number =>\n getDateTimePart({ node, index, separator: '-' })\nconst getDateYear = getDatePart(0)\nconst getDateMonth = getDatePart(1)\nconst getDateDay = getDatePart(2)\n\nconst _datePropGetters: { [key in ValuePropsDate]: (node: ArenaRecordNode) => number } = {\n [ValuePropsDate.day]: getDateDay,\n [ValuePropsDate.month]: getDateMonth,\n [ValuePropsDate.year]: getDateYear,\n}\n\n// File\nconst getFileName = (node: ArenaRecordNode): string | undefined =>\n getNodeValuePropRaw({ node, prop: ValuePropsFile.fileName })\nconst getFileNameCalculated = (node: ArenaRecordNode): string | undefined =>\n getNodeValuePropRaw({ node, prop: ValuePropsFile.fileNameCalculated })\nconst getFileSize = (node: ArenaRecordNode): string | undefined =>\n getNodeValuePropRaw({ node, prop: ValuePropsFile.fileSize })\nconst getFileUuid = (node: ArenaRecordNode): string | undefined =>\n getNodeValuePropRaw({ node, prop: ValuePropsFile.fileUuid })\n\n// Taxon\nconst getTaxonUuid = (node: ArenaRecordNode): string | undefined =>\n getNodeValuePropRaw({ node, prop: ValuePropsTaxon[ValuePropsTaxon.taxonUuid] })\nconst getValueTaxonUuid = (value: any): string | undefined =>\n getValuePropRaw({ value, prop: ValuePropsTaxon[ValuePropsTaxon.taxonUuid] })\nconst getScientificName = (node: ArenaRecordNode): string | undefined =>\n getNodeValuePropRaw({ node, prop: ValuePropsTaxon[ValuePropsTaxon.scientificName] })\nconst getValueScientificName = (value: any): string | undefined =>\n getValuePropRaw({ value, prop: ValuePropsTaxon[ValuePropsTaxon.scientificName] })\nconst getVernacularName = (node: ArenaRecordNode): string | undefined =>\n getNodeValuePropRaw({ node, prop: ValuePropsTaxon[ValuePropsTaxon.vernacularName] })\nconst getValueVernacularName = (value: any): string | undefined =>\n getValuePropRaw({ value, prop: ValuePropsTaxon[ValuePropsTaxon.vernacularName] })\nconst getVernacularNameUuid = (node: ArenaRecordNode): string | undefined =>\n getNodeValuePropRaw({ node, prop: ValuePropsTaxon[ValuePropsTaxon.vernacularNameUuid] })\nconst getValueVernacularNameUuid = (value: any): string | undefined =>\n getValuePropRaw({ value, prop: ValuePropsTaxon[ValuePropsTaxon.vernacularNameUuid] })\n\n// Time\nconst _getTimePart =\n (index: number) =>\n (node: ArenaRecordNode): number =>\n getDateTimePart({ node, index, separator: ':' })\nconst getTimeHour = _getTimePart(0)\nconst getTimeMinute = _getTimePart(1)\nconst getTimeSeconds = (node: ArenaRecordNode): number => {\n const parts = (node.value ?? '').split(':')\n return parts.length > 2 ? _getTimePart(2)(node) : 0\n}\n\nconst _timePropGetters: { [key in ValuePropsTime]: any } = {\n [ValuePropsTime.hour]: getTimeHour,\n [ValuePropsTime.minute]: getTimeMinute,\n [ValuePropsTime.seconds]: getTimeSeconds,\n}\n\nconst _valuePropGetters: { [key in NodeDefType]?: (prop: string) => (node: ArenaRecordNode) => number } = {\n [NodeDefType.date]: (prop: string) => _datePropGetters[ValuePropsDate[prop as keyof typeof ValuePropsDate]],\n [NodeDefType.time]: (prop: string) => _timePropGetters[ValuePropsTime[prop as keyof typeof ValuePropsTime]],\n}\n\nconst isValueProp = (params: { nodeDef: NodeDef<NodeDefType, NodeDefProps>; prop: string }): boolean => {\n const { nodeDef, prop } = params\n return Object.values(ValuePropsByType[nodeDef.type] ?? {}).includes(prop)\n}\n\nconst getNodeValueProp = (params: {\n nodeDef: NodeDef<NodeDefType, NodeDefProps>\n node: ArenaRecordNode\n prop: string\n}): any => {\n const { nodeDef, node, prop } = params\n const propGetter: any = _valuePropGetters[nodeDef.type]\n return propGetter ? propGetter(prop)(node) : getNodeValuePropRaw({ node, prop })\n}\n\n// value compare\n\nconst singlePropValueEqualComparator = (params: NodeValuesCompareParams): boolean => {\n const { value, valueSearch } = params\n return value === valueSearch || String(value) === String(valueSearch)\n}\n\nconst extractCategoryItemUuidFromValue = (params: NodeValuesCompareParams): string | undefined => {\n const { survey, nodeDef, record, parentNode, value } = params\n const valueItemUuid = getValueItemUuid(value)\n if (valueItemUuid) {\n return valueItemUuid\n }\n // find itemUuid by code\n const valueCode = getValueCode(value)\n if (Objects.isEmpty(valueCode)) return undefined\n\n const nodeDefCode = nodeDef as NodeDefCode\n const code = valueCode!\n const categoryUuid = nodeDefCode.props?.categoryUuid\n const levelIndex = Surveys.getNodeDefCategoryLevelIndex({ survey, nodeDef: nodeDefCode })\n if (levelIndex === 0) {\n return Surveys.getCategoryItemUuidByCode({ survey, categoryUuid, parentItemUuid: undefined, code })\n }\n if (!record || !parentNode) return undefined\n\n const parentCodeAttribute = Records.getParentCodeAttribute(parentNode, nodeDef as NodeDefCode)(record)\n if (!parentCodeAttribute) return undefined\n\n const parentItemUuid = getItemUuid(parentCodeAttribute)\n if (!parentItemUuid) return undefined\n\n return Surveys.getCategoryItemUuidByCode({\n survey,\n categoryUuid,\n parentItemUuid,\n code,\n })\n}\n\nconst extractCategoryItemCodeFromValue = (params: { survey: Survey; value: any }): string | undefined => {\n const { survey, value } = params\n const itemUuid = getValueItemUuid(value)\n if (itemUuid) {\n const item = Surveys.getCategoryItemByUuid({ survey, itemUuid })\n return item ? CategoryItems.getCode(item) : undefined\n }\n return getValueCode(value)\n}\n\ntype DateTimeFormats = { formatsSource: DateFormats[]; formatTo: DateFormats }\n\nconst dateFormats: DateTimeFormats = {\n formatsSource: [DateFormats.dateDisplay, DateFormats.dateStorage],\n formatTo: DateFormats.dateStorage,\n}\n\nconst timeFormats: DateTimeFormats = {\n formatsSource: [DateFormats.timeStorage, DateFormats.timeWithSeconds],\n formatTo: DateFormats.timeWithSeconds,\n}\n\nconst toDateTime = (params: DateTimeFormats & { value: any }) => {\n const { formatsSource, formatTo, value } = params\n if (value instanceof Date) {\n return Dates.format(value, formatTo)\n }\n const formatFrom = formatsSource.find((format) => Dates.isValidDateInFormat(value, format))\n return formatFrom ? Dates.convertDate({ dateStr: value, formatFrom, formatTo }) : null\n}\n\nconst dateTimeComparator =\n (formats: DateTimeFormats) =>\n (paramsComparator: NodeValuesCompareParams): boolean => {\n const { value, valueSearch } = paramsComparator\n const dateTime = toDateTime({ ...formats, value })\n const dateTimeSearch = toDateTime({ ...formats, value: valueSearch })\n return !!dateTime && !!dateTimeSearch && dateTime === dateTimeSearch\n }\n\nconst valueComparatorByNodeDefType: { [key in NodeDefType]?: (params: NodeValuesCompareParams) => boolean } = {\n [NodeDefType.boolean]: singlePropValueEqualComparator,\n [NodeDefType.code]: (params: NodeValuesCompareParams): boolean => {\n const { survey, record, value, valueSearch, strict } = params\n if (!strict || !record) {\n // compare just codes (record not available, tricky to find the \"correct\" category item without knowing its parent item)\n const code = extractCategoryItemCodeFromValue({ survey, value })\n const codeSearch = extractCategoryItemCodeFromValue({ survey, value: valueSearch })\n return !!code && !!codeSearch && code === codeSearch\n }\n const itemUuid = extractCategoryItemUuidFromValue(params)\n const itemUuidSearch = extractCategoryItemUuidFromValue({\n ...params,\n value: valueSearch,\n })\n return !!itemUuidSearch && !!itemUuid && itemUuidSearch === itemUuid\n },\n [NodeDefType.coordinate]: (params: NodeValuesCompareParams) => {\n const { value, valueSearch } = params\n return Objects.isEqual(value, valueSearch)\n },\n [NodeDefType.date]: dateTimeComparator(dateFormats),\n [NodeDefType.decimal]: singlePropValueEqualComparator,\n [NodeDefType.integer]: singlePropValueEqualComparator,\n [NodeDefType.taxon]: ({ value, valueSearch }) => {\n if (value === valueSearch) return true\n if (!value) return false\n if (!valueSearch) return false\n return value[ValuePropsTaxon.taxonUuid] === valueSearch[ValuePropsTaxon.taxonUuid]\n },\n [NodeDefType.text]: singlePropValueEqualComparator,\n [NodeDefType.time]: dateTimeComparator(timeFormats),\n}\n\ninterface NodeValuesCompareParams {\n survey: Survey\n nodeDef: NodeDef<NodeDefType, any>\n value: any\n valueSearch: any\n record?: ArenaRecord\n parentNode?: ArenaRecordNode\n strict?: boolean\n}\n\nconst isValueEqual = (params: NodeValuesCompareParams): boolean => {\n const { survey, nodeDef, value, valueSearch, record = undefined, parentNode = undefined, strict = false } = params\n if (value === valueSearch) return true\n if (Objects.isEmpty(value) || Objects.isEmpty(valueSearch)) return false\n\n const valueComparator = valueComparatorByNodeDefType[nodeDef.type]\n if (!valueComparator) {\n return Objects.isEqual(value, valueSearch)\n }\n return valueComparator({ survey, nodeDef, record, parentNode, value, valueSearch, strict })\n}\n\nconst primitiveTypes = new Set(['string', 'number', 'boolean', 'undefined', 'bigint'])\n\n// token of a primitive value, equal for strictly equal (===) values only (distinguishes \"1\" from 1);\n// undefined for non primitive values and NaN (not equal to itself)\nconst toPrimitiveToken = (value: any): string | undefined =>\n (value === null || primitiveTypes.has(typeof value)) && !Number.isNaN(value)\n ? `${typeof value}:${String(value)}`\n : undefined\n\nconst dateTimeKeyExtractor =\n (formats: DateTimeFormats) =>\n ({ value }: { value: any }): string | undefined =>\n toDateTime({ ...formats, value }) || undefined\n\nconst valueEqualityKeyByNodeDefType: {\n [key in NodeDefType]?: (params: { survey: Survey; value: any }) => string | undefined\n} = {\n [NodeDefType.boolean]: ({ value }) => String(value),\n [NodeDefType.code]: ({ survey, value }) => {\n const code = extractCategoryItemCodeFromValue({ survey, value })\n return code ? toPrimitiveToken(code) : undefined\n },\n [NodeDefType.date]: dateTimeKeyExtractor(dateFormats),\n [NodeDefType.decimal]: ({ value }) => String(value),\n [NodeDefType.integer]: ({ value }) => String(value),\n [NodeDefType.taxon]: ({ value }) => toPrimitiveToken(value[ValuePropsTaxon.taxonUuid]),\n [NodeDefType.text]: ({ value }) => String(value),\n [NodeDefType.time]: dateTimeKeyExtractor(timeFormats),\n}\n\n/**\n * Returns a key such that two values have the same key if and only if they are equal according to isValueEqual\n * (not strict, without record), or undefined if such a key cannot be built for the value (e.g. NaN, empty objects, code values\n * without code, invalid dates, coordinates): such values must be compared with isValueEqual.\n * Not exported in NodeValues: internal, used to look up duplicate values without comparing every pair of values.\n */\nexport const getValueEqualityKey = (params: {\n survey: Survey\n nodeDef: NodeDef<NodeDefType, any>\n value: any\n}): string | undefined => {\n const { survey, nodeDef, value } = params\n if (Objects.isEmpty(value)) {\n // empty values are equal only if identical (===)\n const token = toPrimitiveToken(value)\n return token === undefined ? undefined : `empty:${token}`\n }\n const keyExtractor = valueEqualityKeyByNodeDefType[nodeDef.type]\n const key = keyExtractor?.({ survey, value })\n return key === undefined ? undefined : `value:${key}`\n}\n\nconst getValueAsPoint = (params: { survey: Survey; node: ArenaRecordNode }): Point | null => {\n const { survey, node } = params\n const nodeValue = node?.value\n\n if (!nodeValue) return null\n\n const nodeDef = Surveys.getNodeDefByUuid({ survey, uuid: node.nodeDefUuid })\n if (nodeDef.type === NodeDefType.coordinate) {\n const { x, y, srs } = nodeValue as NodeValueCoordinate\n\n const srsObject = Surveys.getSRSByCode(srs)(survey)\n if (!srsObject) return null\n\n return PointFactory.createInstance({ srs, x, y })\n }\n return null\n}\n\nexport const NodeValues = {\n ValuePropsByType,\n ValuePropsCode,\n ValuePropsCoordinate,\n ValuePropsDate,\n ValuePropsFile,\n ValuePropsTaxon,\n ValuePropsTime,\n\n getNodeValueProp,\n isValueProp,\n\n // coordinate\n getValueAsPoint,\n\n // code\n newCodeValue,\n getItemUuid,\n getValueCode,\n getValueItemUuid,\n\n // date\n getDateYear,\n getDateMonth,\n getDateDay,\n\n // file\n getFileName,\n getFileNameCalculated,\n getFileSize,\n getFileUuid,\n\n // taxon\n getTaxonUuid,\n getValueTaxonUuid,\n getScientificName,\n getValueScientificName,\n getVernacularName,\n getValueVernacularName,\n getVernacularNameUuid,\n getValueVernacularNameUuid,\n\n // time\n getTimeHour,\n getTimeMinute,\n getTimeSeconds,\n\n // utils\n isValueEqual,\n}\n"]}
@@ -1,12 +1,15 @@
1
1
  import { Records } from '../records';
2
2
  import { NodeDefs } from '../../nodeDef';
3
3
  import { Nodes, NodeValues } from '../../node';
4
+ import { getValueEqualityKey } from '../../node/nodeValues';
4
5
  import { ValidationResultFactory, ValidationSeverity } from '../../validation';
5
6
  import { Surveys } from '../../survey';
6
7
  import { Objects } from '../../utils';
7
8
  export const createEntityKeysCache = () => ({
8
9
  keyDefsByEntityDefUuid: new Map(),
9
10
  keyValuesByEntityUuid: new Map(),
11
+ equalityKeyByEntityUuid: new Map(),
12
+ siblingEntitiesKeysIndexByParentAndDefUuid: new Map(),
10
13
  });
11
14
  const _getKeyDefs = (params) => {
12
15
  const { survey, entityDef, cache } = params;
@@ -26,41 +29,95 @@ const _getKeyValuesByDefUuid = (params) => {
26
29
  }
27
30
  return keyValues;
28
31
  };
32
+ const _getEqualityKey = (params) => {
33
+ const { survey, entity, keyDefs, keyValuesByDefUuid, cache } = params;
34
+ if (cache.equalityKeyByEntityUuid.has(entity.uuid))
35
+ return cache.equalityKeyByEntityUuid.get(entity.uuid);
36
+ const keyParts = [];
37
+ for (const keyDef of keyDefs) {
38
+ const keyPart = getValueEqualityKey({ survey, nodeDef: keyDef, value: keyValuesByDefUuid[keyDef.uuid] });
39
+ if (keyPart === undefined) {
40
+ keyParts.length = 0;
41
+ break;
42
+ }
43
+ keyParts.push(keyPart);
44
+ }
45
+ const equalityKey = keyParts.length > 0 ? JSON.stringify(keyParts) : undefined;
46
+ cache.equalityKeyByEntityUuid.set(entity.uuid, equalityKey);
47
+ return equalityKey;
48
+ };
49
+ const _areKeyValuesEqual = (params) => {
50
+ const { survey, keyDefs, keyValuesByDefUuid, siblingKeyValuesByDefUuid } = params;
51
+ return keyDefs.every((keyDef) => NodeValues.isValueEqual({
52
+ survey,
53
+ nodeDef: keyDef,
54
+ value: keyValuesByDefUuid[keyDef.uuid],
55
+ valueSearch: siblingKeyValuesByDefUuid[keyDef.uuid],
56
+ }));
57
+ };
58
+ const _getSiblingEntitiesKeysIndex = (params) => {
59
+ const { survey, record, nodeParent, entityDefUuid, keyDefs, cache } = params;
60
+ const indexKey = `${nodeParent.uuid}_${entityDefUuid}`;
61
+ let index = cache.siblingEntitiesKeysIndexByParentAndDefUuid.get(indexKey);
62
+ if (!index) {
63
+ index = { entitiesByEqualityKey: new Map(), entitiesWithoutEqualityKey: [] };
64
+ for (const siblingEntity of Records.getChildren(nodeParent, entityDefUuid)(record)) {
65
+ const keyValuesByDefUuid = _getKeyValuesByDefUuid({ survey, record, entity: siblingEntity, keyDefs, cache });
66
+ const equalityKey = _getEqualityKey({ survey, entity: siblingEntity, keyDefs, keyValuesByDefUuid, cache });
67
+ if (equalityKey === undefined) {
68
+ index.entitiesWithoutEqualityKey.push(siblingEntity);
69
+ }
70
+ else {
71
+ const entitiesWithSameKey = index.entitiesByEqualityKey.get(equalityKey);
72
+ if (entitiesWithSameKey) {
73
+ entitiesWithSameKey.push(siblingEntity);
74
+ }
75
+ else {
76
+ index.entitiesByEqualityKey.set(equalityKey, [siblingEntity]);
77
+ }
78
+ }
79
+ }
80
+ cache.siblingEntitiesKeysIndexByParentAndDefUuid.set(indexKey, index);
81
+ }
82
+ return index;
83
+ };
29
84
  const _isEntityDuplicate = (params) => {
85
+ var _a;
30
86
  const { survey, entity, record, entityKeysCache: cache } = params;
31
- // 1. get sibling entities
32
87
  const nodeParent = Records.getParent(entity)(record);
33
88
  if (!nodeParent)
34
89
  return false;
35
- const siblingEntities = Records.getChildren(nodeParent, entity.nodeDefUuid)(record).filter((node) => !Nodes.areEqual(entity, node));
36
- // 2. get key values
37
90
  const entityDef = Surveys.getNodeDefByUuid({ survey, uuid: entity.nodeDefUuid });
38
91
  const keyDefs = _getKeyDefs({ survey, entityDef, cache });
39
- if (Objects.isEmpty(siblingEntities) || Objects.isEmpty(keyDefs)) {
92
+ if (Objects.isEmpty(keyDefs))
40
93
  return false;
41
- }
42
94
  const keyValuesByDefUuid = _getKeyValuesByDefUuid({ survey, record, entity, keyDefs, cache });
43
95
  // entities with all key values empty are never duplicates (they are reported as missing values)
44
- return (!Object.values(keyValuesByDefUuid).every(Objects.isEmpty) &&
45
- siblingEntities.some((sibilingEntity) => {
46
- const siblingKeyValuesByDefUuid = _getKeyValuesByDefUuid({
47
- survey,
48
- record,
49
- entity: sibilingEntity,
50
- keyDefs,
51
- cache,
52
- });
53
- return keyDefs.every((keyDef) => {
54
- const keyValue = keyValuesByDefUuid[keyDef.uuid];
55
- const siblingKeyValue = siblingKeyValuesByDefUuid[keyDef.uuid];
56
- return NodeValues.isValueEqual({
57
- survey,
58
- nodeDef: keyDef,
59
- value: keyValue,
60
- valueSearch: siblingKeyValue,
61
- });
62
- });
63
- }));
96
+ if (Object.values(keyValuesByDefUuid).every(Objects.isEmpty))
97
+ return false;
98
+ const isDuplicateOf = (siblingEntity) => !Nodes.areEqual(entity, siblingEntity) &&
99
+ _areKeyValuesEqual({
100
+ survey,
101
+ keyDefs,
102
+ keyValuesByDefUuid,
103
+ siblingKeyValuesByDefUuid: _getKeyValuesByDefUuid({ survey, record, entity: siblingEntity, keyDefs, cache }),
104
+ });
105
+ const equalityKey = cache ? _getEqualityKey({ survey, entity, keyDefs, keyValuesByDefUuid, cache }) : undefined;
106
+ if (!cache || equalityKey === undefined) {
107
+ // compare the key values with the ones of every sibling entity
108
+ return Records.getChildren(nodeParent, entity.nodeDefUuid)(record).some(isDuplicateOf);
109
+ }
110
+ // look up the sibling entities with the same key values in the index (built once for all the siblings)
111
+ const index = _getSiblingEntitiesKeysIndex({
112
+ survey,
113
+ record,
114
+ nodeParent,
115
+ entityDefUuid: entity.nodeDefUuid,
116
+ keyDefs,
117
+ cache,
118
+ });
119
+ return (!!((_a = index.entitiesByEqualityKey.get(equalityKey)) === null || _a === void 0 ? void 0 : _a.some((siblingEntity) => !Nodes.areEqual(entity, siblingEntity))) ||
120
+ index.entitiesWithoutEqualityKey.some(isDuplicateOf));
64
121
  };
65
122
  const isNodeDefToBeValidated = (params) => {
66
123
  const { survey, nodeDef } = params;
@@ -1 +1 @@
1
- {"version":3,"file":"attributeKeyValidator.js","sourceRoot":"","sources":["../../../../src/record/recordValidator/attributeKeyValidator.ts"],"names":[],"mappings":"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","sourcesContent":["import { ArenaRecord } from '../record'\nimport { Records } from '../records'\nimport { NodeDef, NodeDefs, NodeDefProps, NodeDefType } from '../../nodeDef'\nimport { ArenaRecordNode, Nodes, NodeValues } from '../../node'\nimport { ValidationResult, ValidationResultFactory, ValidationSeverity } from '../../validation'\nimport { Survey, Surveys } from '../../survey'\n\nimport { Dictionary } from '../../common'\nimport { Objects } from '../../utils'\n\n/**\n * Cache of entity key values (and key defs by entity def) used during a single validation run (the record doesn't change):\n * without it, validating the keys of S sibling entities would compute the key values of every sibling S times.\n */\nexport type EntityKeysCache = {\n keyDefsByEntityDefUuid: Map<string, NodeDef<NodeDefType, NodeDefProps>[]>\n keyValuesByEntityUuid: Map<string, Dictionary<any>>\n}\n\nexport const createEntityKeysCache = (): EntityKeysCache => ({\n keyDefsByEntityDefUuid: new Map(),\n keyValuesByEntityUuid: new Map(),\n})\n\nconst _getKeyDefs = (params: {\n survey: Survey\n entityDef: NodeDef<NodeDefType, NodeDefProps>\n cache?: EntityKeysCache\n}): NodeDef<NodeDefType, NodeDefProps>[] => {\n const { survey, entityDef, cache } = params\n let keyDefs = cache?.keyDefsByEntityDefUuid.get(entityDef.uuid)\n if (!keyDefs) {\n keyDefs = Surveys.getNodeDefKeys({ survey, nodeDef: entityDef })\n cache?.keyDefsByEntityDefUuid.set(entityDef.uuid, keyDefs)\n }\n return keyDefs\n}\n\nconst _getKeyValuesByDefUuid = (params: {\n survey: Survey\n record: ArenaRecord\n entity: ArenaRecordNode\n keyDefs: NodeDef<NodeDefType, NodeDefProps>[]\n cache?: EntityKeysCache\n}): Dictionary<any> => {\n const { survey, record, entity, keyDefs, cache } = params\n let keyValues = cache?.keyValuesByEntityUuid.get(entity.uuid)\n if (!keyValues) {\n keyValues = Records.getEntityKeyValuesByDefUuid(survey, entity, undefined, keyDefs)(record)\n cache?.keyValuesByEntityUuid.set(entity.uuid, keyValues)\n }\n return keyValues\n}\n\nconst _isEntityDuplicate = (params: {\n survey: Survey\n record: ArenaRecord\n entity: ArenaRecordNode\n entityKeysCache?: EntityKeysCache\n}): boolean => {\n const { survey, entity, record, entityKeysCache: cache } = params\n // 1. get sibling entities\n const nodeParent = Records.getParent(entity)(record)\n if (!nodeParent) return false\n\n const siblingEntities = Records.getChildren(\n nodeParent,\n entity.nodeDefUuid\n )(record).filter((node) => !Nodes.areEqual(entity, node))\n\n // 2. get key values\n const entityDef = Surveys.getNodeDefByUuid({ survey, uuid: entity.nodeDefUuid })\n const keyDefs = _getKeyDefs({ survey, entityDef, cache })\n\n if (Objects.isEmpty(siblingEntities) || Objects.isEmpty(keyDefs)) {\n return false\n }\n const keyValuesByDefUuid = _getKeyValuesByDefUuid({ survey, record, entity, keyDefs, cache })\n // entities with all key values empty are never duplicates (they are reported as missing values)\n return (\n !Object.values(keyValuesByDefUuid).every(Objects.isEmpty) &&\n siblingEntities.some((sibilingEntity) => {\n const siblingKeyValuesByDefUuid = _getKeyValuesByDefUuid({\n survey,\n record,\n entity: sibilingEntity,\n keyDefs,\n cache,\n })\n return keyDefs.every((keyDef) => {\n const keyValue = keyValuesByDefUuid[keyDef.uuid]\n const siblingKeyValue = siblingKeyValuesByDefUuid[keyDef.uuid]\n return NodeValues.isValueEqual({\n survey,\n nodeDef: keyDef,\n value: keyValue,\n valueSearch: siblingKeyValue,\n })\n })\n })\n )\n}\n\nconst isNodeDefToBeValidated = (params: { survey: Survey; nodeDef: NodeDef<NodeDefType, NodeDefProps> }): boolean => {\n const { survey, nodeDef } = params\n if (!NodeDefs.isKey(nodeDef)) return false\n\n const nodeDefParent = Surveys.getNodeDefParent({ survey, nodeDef })\n // root entity key attributes will be validated by another validator\n return !!nodeDefParent && !NodeDefs.isRoot(nodeDefParent)\n}\n\nconst validateAttributeKey =\n (params: {\n survey: Survey\n record: ArenaRecord\n nodeDef: NodeDef<NodeDefType, NodeDefProps>\n entityKeysCache?: EntityKeysCache\n }) =>\n (_propName: string, node: ArenaRecordNode): Promise<ValidationResult> => {\n const { survey, nodeDef, record, entityKeysCache } = params\n\n if (isNodeDefToBeValidated({ survey, nodeDef })) {\n const entity = Records.getParent(node)(record)\n if (entity && _isEntityDuplicate({ survey, record, entity, entityKeysCache })) {\n return Promise.resolve(\n ValidationResultFactory.createInstance({\n valid: false,\n severity: ValidationSeverity.error,\n key: 'record.entity.keyDuplicate',\n })\n )\n }\n }\n\n return Promise.resolve(ValidationResultFactory.createInstance())\n }\n\nexport const AttributeKeyValidator = {\n validateAttributeKey,\n}\n"]}
1
+ {"version":3,"file":"attributeKeyValidator.js","sourceRoot":"","sources":["../../../../src/record/recordValidator/attributeKeyValidator.ts"],"names":[],"mappings":"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","sourcesContent":["import { ArenaRecord } from '../record'\nimport { Records } from '../records'\nimport { NodeDef, NodeDefs, NodeDefProps, NodeDefType } from '../../nodeDef'\nimport { ArenaRecordNode, Nodes, NodeValues } from '../../node'\nimport { getValueEqualityKey } from '../../node/nodeValues'\nimport { ValidationResult, ValidationResultFactory, ValidationSeverity } from '../../validation'\nimport { Survey, Surveys } from '../../survey'\n\nimport { Dictionary } from '../../common'\nimport { Objects } from '../../utils'\n\n/**\n * Sibling entities (same parent and entity def) indexed by the equality key of their key values:\n * entities with the same equality key are duplicates; entities whose equality key cannot be built (see getValueEqualityKey)\n * are compared with every sibling.\n */\ntype SiblingEntitiesKeysIndex = {\n entitiesByEqualityKey: Map<string, ArenaRecordNode[]>\n entitiesWithoutEqualityKey: ArenaRecordNode[]\n}\n\n/**\n * Cache of entity key values (and key defs by entity def) used during a single validation run (the record doesn't change):\n * without it, validating the keys of S sibling entities would compute the key values of every sibling S times\n * and compare them S times with the key values of every sibling.\n */\nexport type EntityKeysCache = {\n keyDefsByEntityDefUuid: Map<string, NodeDef<NodeDefType, NodeDefProps>[]>\n keyValuesByEntityUuid: Map<string, Dictionary<any>>\n equalityKeyByEntityUuid: Map<string, string | undefined>\n siblingEntitiesKeysIndexByParentAndDefUuid: Map<string, SiblingEntitiesKeysIndex>\n}\n\nexport const createEntityKeysCache = (): EntityKeysCache => ({\n keyDefsByEntityDefUuid: new Map(),\n keyValuesByEntityUuid: new Map(),\n equalityKeyByEntityUuid: new Map(),\n siblingEntitiesKeysIndexByParentAndDefUuid: new Map(),\n})\n\nconst _getKeyDefs = (params: {\n survey: Survey\n entityDef: NodeDef<NodeDefType, NodeDefProps>\n cache?: EntityKeysCache\n}): NodeDef<NodeDefType, NodeDefProps>[] => {\n const { survey, entityDef, cache } = params\n let keyDefs = cache?.keyDefsByEntityDefUuid.get(entityDef.uuid)\n if (!keyDefs) {\n keyDefs = Surveys.getNodeDefKeys({ survey, nodeDef: entityDef })\n cache?.keyDefsByEntityDefUuid.set(entityDef.uuid, keyDefs)\n }\n return keyDefs\n}\n\nconst _getKeyValuesByDefUuid = (params: {\n survey: Survey\n record: ArenaRecord\n entity: ArenaRecordNode\n keyDefs: NodeDef<NodeDefType, NodeDefProps>[]\n cache?: EntityKeysCache\n}): Dictionary<any> => {\n const { survey, record, entity, keyDefs, cache } = params\n let keyValues = cache?.keyValuesByEntityUuid.get(entity.uuid)\n if (!keyValues) {\n keyValues = Records.getEntityKeyValuesByDefUuid(survey, entity, undefined, keyDefs)(record)\n cache?.keyValuesByEntityUuid.set(entity.uuid, keyValues)\n }\n return keyValues\n}\n\nconst _getEqualityKey = (params: {\n survey: Survey\n entity: ArenaRecordNode\n keyDefs: NodeDef<NodeDefType, NodeDefProps>[]\n keyValuesByDefUuid: Dictionary<any>\n cache: EntityKeysCache\n}): string | undefined => {\n const { survey, entity, keyDefs, keyValuesByDefUuid, cache } = params\n if (cache.equalityKeyByEntityUuid.has(entity.uuid)) return cache.equalityKeyByEntityUuid.get(entity.uuid)\n\n const keyParts: string[] = []\n for (const keyDef of keyDefs) {\n const keyPart = getValueEqualityKey({ survey, nodeDef: keyDef, value: keyValuesByDefUuid[keyDef.uuid] })\n if (keyPart === undefined) {\n keyParts.length = 0\n break\n }\n keyParts.push(keyPart)\n }\n const equalityKey = keyParts.length > 0 ? JSON.stringify(keyParts) : undefined\n cache.equalityKeyByEntityUuid.set(entity.uuid, equalityKey)\n return equalityKey\n}\n\nconst _areKeyValuesEqual = (params: {\n survey: Survey\n keyDefs: NodeDef<NodeDefType, NodeDefProps>[]\n keyValuesByDefUuid: Dictionary<any>\n siblingKeyValuesByDefUuid: Dictionary<any>\n}): boolean => {\n const { survey, keyDefs, keyValuesByDefUuid, siblingKeyValuesByDefUuid } = params\n return keyDefs.every((keyDef) =>\n NodeValues.isValueEqual({\n survey,\n nodeDef: keyDef,\n value: keyValuesByDefUuid[keyDef.uuid],\n valueSearch: siblingKeyValuesByDefUuid[keyDef.uuid],\n })\n )\n}\n\nconst _getSiblingEntitiesKeysIndex = (params: {\n survey: Survey\n record: ArenaRecord\n nodeParent: ArenaRecordNode\n entityDefUuid: string\n keyDefs: NodeDef<NodeDefType, NodeDefProps>[]\n cache: EntityKeysCache\n}): SiblingEntitiesKeysIndex => {\n const { survey, record, nodeParent, entityDefUuid, keyDefs, cache } = params\n const indexKey = `${nodeParent.uuid}_${entityDefUuid}`\n let index = cache.siblingEntitiesKeysIndexByParentAndDefUuid.get(indexKey)\n if (!index) {\n index = { entitiesByEqualityKey: new Map(), entitiesWithoutEqualityKey: [] }\n for (const siblingEntity of Records.getChildren(nodeParent, entityDefUuid)(record)) {\n const keyValuesByDefUuid = _getKeyValuesByDefUuid({ survey, record, entity: siblingEntity, keyDefs, cache })\n const equalityKey = _getEqualityKey({ survey, entity: siblingEntity, keyDefs, keyValuesByDefUuid, cache })\n if (equalityKey === undefined) {\n index.entitiesWithoutEqualityKey.push(siblingEntity)\n } else {\n const entitiesWithSameKey = index.entitiesByEqualityKey.get(equalityKey)\n if (entitiesWithSameKey) {\n entitiesWithSameKey.push(siblingEntity)\n } else {\n index.entitiesByEqualityKey.set(equalityKey, [siblingEntity])\n }\n }\n }\n cache.siblingEntitiesKeysIndexByParentAndDefUuid.set(indexKey, index)\n }\n return index\n}\n\nconst _isEntityDuplicate = (params: {\n survey: Survey\n record: ArenaRecord\n entity: ArenaRecordNode\n entityKeysCache?: EntityKeysCache\n}): boolean => {\n const { survey, entity, record, entityKeysCache: cache } = params\n const nodeParent = Records.getParent(entity)(record)\n if (!nodeParent) return false\n\n const entityDef = Surveys.getNodeDefByUuid({ survey, uuid: entity.nodeDefUuid })\n const keyDefs = _getKeyDefs({ survey, entityDef, cache })\n if (Objects.isEmpty(keyDefs)) return false\n\n const keyValuesByDefUuid = _getKeyValuesByDefUuid({ survey, record, entity, keyDefs, cache })\n // entities with all key values empty are never duplicates (they are reported as missing values)\n if (Object.values(keyValuesByDefUuid).every(Objects.isEmpty)) return false\n\n const isDuplicateOf = (siblingEntity: ArenaRecordNode): boolean =>\n !Nodes.areEqual(entity, siblingEntity) &&\n _areKeyValuesEqual({\n survey,\n keyDefs,\n keyValuesByDefUuid,\n siblingKeyValuesByDefUuid: _getKeyValuesByDefUuid({ survey, record, entity: siblingEntity, keyDefs, cache }),\n })\n\n const equalityKey = cache ? _getEqualityKey({ survey, entity, keyDefs, keyValuesByDefUuid, cache }) : undefined\n if (!cache || equalityKey === undefined) {\n // compare the key values with the ones of every sibling entity\n return Records.getChildren(nodeParent, entity.nodeDefUuid)(record).some(isDuplicateOf)\n }\n // look up the sibling entities with the same key values in the index (built once for all the siblings)\n const index = _getSiblingEntitiesKeysIndex({\n survey,\n record,\n nodeParent,\n entityDefUuid: entity.nodeDefUuid,\n keyDefs,\n cache,\n })\n return (\n !!index.entitiesByEqualityKey.get(equalityKey)?.some((siblingEntity) => !Nodes.areEqual(entity, siblingEntity)) ||\n index.entitiesWithoutEqualityKey.some(isDuplicateOf)\n )\n}\n\nconst isNodeDefToBeValidated = (params: { survey: Survey; nodeDef: NodeDef<NodeDefType, NodeDefProps> }): boolean => {\n const { survey, nodeDef } = params\n if (!NodeDefs.isKey(nodeDef)) return false\n\n const nodeDefParent = Surveys.getNodeDefParent({ survey, nodeDef })\n // root entity key attributes will be validated by another validator\n return !!nodeDefParent && !NodeDefs.isRoot(nodeDefParent)\n}\n\nconst validateAttributeKey =\n (params: {\n survey: Survey\n record: ArenaRecord\n nodeDef: NodeDef<NodeDefType, NodeDefProps>\n entityKeysCache?: EntityKeysCache\n }) =>\n (_propName: string, node: ArenaRecordNode): Promise<ValidationResult> => {\n const { survey, nodeDef, record, entityKeysCache } = params\n\n if (isNodeDefToBeValidated({ survey, nodeDef })) {\n const entity = Records.getParent(node)(record)\n if (entity && _isEntityDuplicate({ survey, record, entity, entityKeysCache })) {\n return Promise.resolve(\n ValidationResultFactory.createInstance({\n valid: false,\n severity: ValidationSeverity.error,\n key: 'record.entity.keyDuplicate',\n })\n )\n }\n }\n\n return Promise.resolve(ValidationResultFactory.createInstance())\n }\n\nexport const AttributeKeyValidator = {\n validateAttributeKey,\n}\n"]}
@@ -3,7 +3,8 @@ import { Point } from '../point';
3
3
  * Parses a point in the format: SRID=SRS_CODE;POINT(X Y)
4
4
  * (X and Y separated by a space). Valid examples are:
5
5
  * - SRID=EPSG:4326;POINT(12.489060 41.882788)
6
- * - SRID=4326;POINT(12 41).
6
+ * - SRID=4326;POINT(12 41)
7
+ * - SRID=4326;POINT(1e-7 41).
7
8
  *
8
9
  * @param {!(string | object)} value - The point to parse. It can be a string or an object.
9
10
  * @returns {Point} - The parsed Point object.
@@ -2,12 +2,15 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.parse = void 0;
4
4
  const pointFactory_1 = require("../pointFactory");
5
- const POINT_REGEX = /SRID=((EPSG:)?(\w+));POINT\((-?\d+(\.\d+)?) (-?\d+(\.\d+)?)\)/;
5
+ // coordinates can be in exponent notation (e.g. 1e-7), as generated by Number.toString or by PostgreSQL for very small or big numbers
6
+ const NUMBER_PATTERN = String.raw `-?\d+(?:\.\d+)?(?:[eE][-+]?\d+)?`;
7
+ const POINT_REGEX = new RegExp(String.raw `SRID=((EPSG:)?(\w+));POINT\((${NUMBER_PATTERN}) (${NUMBER_PATTERN})\)`);
6
8
  /**
7
9
  * Parses a point in the format: SRID=SRS_CODE;POINT(X Y)
8
10
  * (X and Y separated by a space). Valid examples are:
9
11
  * - SRID=EPSG:4326;POINT(12.489060 41.882788)
10
- * - SRID=4326;POINT(12 41).
12
+ * - SRID=4326;POINT(12 41)
13
+ * - SRID=4326;POINT(1e-7 41).
11
14
  *
12
15
  * @param {!(string | object)} value - The point to parse. It can be a string or an object.
13
16
  * @returns {Point} - The parsed Point object.
@@ -23,7 +26,7 @@ const parse = (value) => {
23
26
  return null;
24
27
  const srs = match[3];
25
28
  const x = Number(match[4]);
26
- const y = Number(match[6]);
29
+ const y = Number(match[5]);
27
30
  return pointFactory_1.PointFactory.createInstance({ srs, x, y });
28
31
  }
29
32
  return null;
@@ -1 +1 @@
1
- {"version":3,"file":"parse.js","sourceRoot":"","sources":["../../../src/geo/points/parse.ts"],"names":[],"mappings":";;;AACA,kDAA8C;AAE9C,MAAM,WAAW,GAAG,+DAA+D,CAAA;AAEnF;;;;;;;;GAQG;AACI,MAAM,KAAK,GAAG,CAAC,KAAsB,EAAgB,EAAE;IAC5D,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAA;IAEvB,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAc,CAAA;IAEpD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACrC,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAA;QAEvB,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;QACpB,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;QAC1B,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;QAC1B,OAAO,2BAAY,CAAC,cAAc,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAA;IACnD,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC,CAAA;AAfY,QAAA,KAAK,GAAL,KAAK,CAejB","sourcesContent":["import { Point } from '../point'\nimport { PointFactory } from '../pointFactory'\n\nconst POINT_REGEX = /SRID=((EPSG:)?(\\w+));POINT\\((-?\\d+(\\.\\d+)?) (-?\\d+(\\.\\d+)?)\\)/\n\n/**\n * Parses a point in the format: SRID=SRS_CODE;POINT(X Y)\n * (X and Y separated by a space). Valid examples are:\n * - SRID=EPSG:4326;POINT(12.489060 41.882788)\n * - SRID=4326;POINT(12 41).\n *\n * @param {!(string | object)} value - The point to parse. It can be a string or an object.\n * @returns {Point} - The parsed Point object.\n */\nexport const parse = (value: string | object): Point | null => {\n if (!value) return null\n\n if (typeof value === 'object') return value as Point\n\n if (typeof value === 'string') {\n const match = POINT_REGEX.exec(value)\n if (!match) return null\n\n const srs = match[3]\n const x = Number(match[4])\n const y = Number(match[6])\n return PointFactory.createInstance({ srs, x, y })\n }\n return null\n}\n"]}
1
+ {"version":3,"file":"parse.js","sourceRoot":"","sources":["../../../src/geo/points/parse.ts"],"names":[],"mappings":";;;AACA,kDAA8C;AAE9C,sIAAsI;AACtI,MAAM,cAAc,GAAG,MAAM,CAAC,GAAG,CAAA,kCAAkC,CAAA;AACnE,MAAM,WAAW,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC,GAAG,CAAA,gCAAgC,cAAc,MAAM,cAAc,KAAK,CAAC,CAAA;AAEjH;;;;;;;;;GASG;AACI,MAAM,KAAK,GAAG,CAAC,KAAsB,EAAgB,EAAE;IAC5D,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAA;IAEvB,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAc,CAAA;IAEpD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACrC,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAA;QAEvB,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;QACpB,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;QAC1B,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;QAC1B,OAAO,2BAAY,CAAC,cAAc,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAA;IACnD,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC,CAAA;AAfY,QAAA,KAAK,GAAL,KAAK,CAejB","sourcesContent":["import { Point } from '../point'\nimport { PointFactory } from '../pointFactory'\n\n// coordinates can be in exponent notation (e.g. 1e-7), as generated by Number.toString or by PostgreSQL for very small or big numbers\nconst NUMBER_PATTERN = String.raw`-?\\d+(?:\\.\\d+)?(?:[eE][-+]?\\d+)?`\nconst POINT_REGEX = new RegExp(String.raw`SRID=((EPSG:)?(\\w+));POINT\\((${NUMBER_PATTERN}) (${NUMBER_PATTERN})\\)`)\n\n/**\n * Parses a point in the format: SRID=SRS_CODE;POINT(X Y)\n * (X and Y separated by a space). Valid examples are:\n * - SRID=EPSG:4326;POINT(12.489060 41.882788)\n * - SRID=4326;POINT(12 41)\n * - SRID=4326;POINT(1e-7 41).\n *\n * @param {!(string | object)} value - The point to parse. It can be a string or an object.\n * @returns {Point} - The parsed Point object.\n */\nexport const parse = (value: string | object): Point | null => {\n if (!value) return null\n\n if (typeof value === 'object') return value as Point\n\n if (typeof value === 'string') {\n const match = POINT_REGEX.exec(value)\n if (!match) return null\n\n const srs = match[3]\n const x = Number(match[4])\n const y = Number(match[5])\n return PointFactory.createInstance({ srs, x, y })\n }\n return null\n}\n"]}
@@ -53,6 +53,17 @@ interface NodeValuesCompareParams {
53
53
  strict?: boolean;
54
54
  }
55
55
  declare const isValueEqual: (params: NodeValuesCompareParams) => boolean;
56
+ /**
57
+ * Returns a key such that two values have the same key if and only if they are equal according to isValueEqual
58
+ * (not strict, without record), or undefined if such a key cannot be built for the value (e.g. NaN, empty objects, code values
59
+ * without code, invalid dates, coordinates): such values must be compared with isValueEqual.
60
+ * Not exported in NodeValues: internal, used to look up duplicate values without comparing every pair of values.
61
+ */
62
+ export declare const getValueEqualityKey: (params: {
63
+ survey: Survey;
64
+ nodeDef: NodeDef<NodeDefType, any>;
65
+ value: any;
66
+ }) => string | undefined;
56
67
  declare const getValueAsPoint: (params: {
57
68
  survey: Survey;
58
69
  node: ArenaRecordNode;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.NodeValues = exports.ValuePropsByType = void 0;
3
+ exports.NodeValues = exports.getValueEqualityKey = exports.ValuePropsByType = void 0;
4
4
  const category_1 = require("../category");
5
5
  const geo_1 = require("../geo");
6
6
  const nodeDef_1 = require("../nodeDef");
@@ -150,18 +150,26 @@ const extractCategoryItemCodeFromValue = (params) => {
150
150
  }
151
151
  return getValueCode(value);
152
152
  };
153
- const dateTimeComparator = (params) => (paramsComparator) => {
154
- const { formatsSource, formatTo } = params;
153
+ const dateFormats = {
154
+ formatsSource: [utils_1.DateFormats.dateDisplay, utils_1.DateFormats.dateStorage],
155
+ formatTo: utils_1.DateFormats.dateStorage,
156
+ };
157
+ const timeFormats = {
158
+ formatsSource: [utils_1.DateFormats.timeStorage, utils_1.DateFormats.timeWithSeconds],
159
+ formatTo: utils_1.DateFormats.timeWithSeconds,
160
+ };
161
+ const toDateTime = (params) => {
162
+ const { formatsSource, formatTo, value } = params;
163
+ if (value instanceof Date) {
164
+ return utils_1.Dates.format(value, formatTo);
165
+ }
166
+ const formatFrom = formatsSource.find((format) => utils_1.Dates.isValidDateInFormat(value, format));
167
+ return formatFrom ? utils_1.Dates.convertDate({ dateStr: value, formatFrom, formatTo }) : null;
168
+ };
169
+ const dateTimeComparator = (formats) => (paramsComparator) => {
155
170
  const { value, valueSearch } = paramsComparator;
156
- const toDateTime = (val) => {
157
- if (val instanceof Date) {
158
- return utils_1.Dates.format(val, formatTo);
159
- }
160
- const formatFrom = formatsSource.find((format) => utils_1.Dates.isValidDateInFormat(val, format));
161
- return formatFrom ? utils_1.Dates.convertDate({ dateStr: val, formatFrom, formatTo }) : null;
162
- };
163
- const dateTime = toDateTime(value);
164
- const dateTimeSearch = toDateTime(valueSearch);
171
+ const dateTime = toDateTime(Object.assign(Object.assign({}, formats), { value }));
172
+ const dateTimeSearch = toDateTime(Object.assign(Object.assign({}, formats), { value: valueSearch }));
165
173
  return !!dateTime && !!dateTimeSearch && dateTime === dateTimeSearch;
166
174
  };
167
175
  const valueComparatorByNodeDefType = {
@@ -182,10 +190,7 @@ const valueComparatorByNodeDefType = {
182
190
  const { value, valueSearch } = params;
183
191
  return utils_1.Objects.isEqual(value, valueSearch);
184
192
  },
185
- [nodeDef_1.NodeDefType.date]: dateTimeComparator({
186
- formatsSource: [utils_1.DateFormats.dateDisplay, utils_1.DateFormats.dateStorage],
187
- formatTo: utils_1.DateFormats.dateStorage,
188
- }),
193
+ [nodeDef_1.NodeDefType.date]: dateTimeComparator(dateFormats),
189
194
  [nodeDef_1.NodeDefType.decimal]: singlePropValueEqualComparator,
190
195
  [nodeDef_1.NodeDefType.integer]: singlePropValueEqualComparator,
191
196
  [nodeDef_1.NodeDefType.taxon]: ({ value, valueSearch }) => {
@@ -198,10 +203,7 @@ const valueComparatorByNodeDefType = {
198
203
  return value[nodeValueProps_1.ValuePropsTaxon.taxonUuid] === valueSearch[nodeValueProps_1.ValuePropsTaxon.taxonUuid];
199
204
  },
200
205
  [nodeDef_1.NodeDefType.text]: singlePropValueEqualComparator,
201
- [nodeDef_1.NodeDefType.time]: dateTimeComparator({
202
- formatsSource: [utils_1.DateFormats.timeStorage, utils_1.DateFormats.timeWithSeconds],
203
- formatTo: utils_1.DateFormats.timeWithSeconds,
204
- }),
206
+ [nodeDef_1.NodeDefType.time]: dateTimeComparator(timeFormats),
205
207
  };
206
208
  const isValueEqual = (params) => {
207
209
  const { survey, nodeDef, value, valueSearch, record = undefined, parentNode = undefined, strict = false } = params;
@@ -215,6 +217,44 @@ const isValueEqual = (params) => {
215
217
  }
216
218
  return valueComparator({ survey, nodeDef, record, parentNode, value, valueSearch, strict });
217
219
  };
220
+ const primitiveTypes = new Set(['string', 'number', 'boolean', 'undefined', 'bigint']);
221
+ // token of a primitive value, equal for strictly equal (===) values only (distinguishes "1" from 1);
222
+ // undefined for non primitive values and NaN (not equal to itself)
223
+ const toPrimitiveToken = (value) => (value === null || primitiveTypes.has(typeof value)) && !Number.isNaN(value)
224
+ ? `${typeof value}:${String(value)}`
225
+ : undefined;
226
+ const dateTimeKeyExtractor = (formats) => ({ value }) => toDateTime(Object.assign(Object.assign({}, formats), { value })) || undefined;
227
+ const valueEqualityKeyByNodeDefType = {
228
+ [nodeDef_1.NodeDefType.boolean]: ({ value }) => String(value),
229
+ [nodeDef_1.NodeDefType.code]: ({ survey, value }) => {
230
+ const code = extractCategoryItemCodeFromValue({ survey, value });
231
+ return code ? toPrimitiveToken(code) : undefined;
232
+ },
233
+ [nodeDef_1.NodeDefType.date]: dateTimeKeyExtractor(dateFormats),
234
+ [nodeDef_1.NodeDefType.decimal]: ({ value }) => String(value),
235
+ [nodeDef_1.NodeDefType.integer]: ({ value }) => String(value),
236
+ [nodeDef_1.NodeDefType.taxon]: ({ value }) => toPrimitiveToken(value[nodeValueProps_1.ValuePropsTaxon.taxonUuid]),
237
+ [nodeDef_1.NodeDefType.text]: ({ value }) => String(value),
238
+ [nodeDef_1.NodeDefType.time]: dateTimeKeyExtractor(timeFormats),
239
+ };
240
+ /**
241
+ * Returns a key such that two values have the same key if and only if they are equal according to isValueEqual
242
+ * (not strict, without record), or undefined if such a key cannot be built for the value (e.g. NaN, empty objects, code values
243
+ * without code, invalid dates, coordinates): such values must be compared with isValueEqual.
244
+ * Not exported in NodeValues: internal, used to look up duplicate values without comparing every pair of values.
245
+ */
246
+ const getValueEqualityKey = (params) => {
247
+ const { survey, nodeDef, value } = params;
248
+ if (utils_1.Objects.isEmpty(value)) {
249
+ // empty values are equal only if identical (===)
250
+ const token = toPrimitiveToken(value);
251
+ return token === undefined ? undefined : `empty:${token}`;
252
+ }
253
+ const keyExtractor = valueEqualityKeyByNodeDefType[nodeDef.type];
254
+ const key = keyExtractor === null || keyExtractor === void 0 ? void 0 : keyExtractor({ survey, value });
255
+ return key === undefined ? undefined : `value:${key}`;
256
+ };
257
+ exports.getValueEqualityKey = getValueEqualityKey;
218
258
  const getValueAsPoint = (params) => {
219
259
  const { survey, node } = params;
220
260
  const nodeValue = node === null || node === void 0 ? void 0 : node.value;
@@ -1 +1 @@
1
- {"version":3,"file":"nodeValues.js","sourceRoot":"","sources":["../../src/node/nodeValues.ts"],"names":[],"mappings":"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","sourcesContent":["import { CategoryItems } from '../category'\nimport { Point, PointFactory } from '../geo'\nimport { NodeDef, NodeDefCode, NodeDefProps, NodeDefType } from '../nodeDef'\nimport { ArenaRecord, Records } from '../record'\nimport { Survey, Surveys } from '../survey'\nimport { DateFormats, Dates, Objects } from '../utils'\nimport { ArenaRecordNode } from './node'\nimport { NodeValueCoordinate } from './nodeValue/coordinate'\nimport {\n ValuePropsCode,\n ValuePropsCoordinate,\n ValuePropsDate,\n ValuePropsFile,\n ValuePropsTaxon,\n ValuePropsTime,\n} from './nodeValueProps'\n\n/**\n * Props of node value indexed by node def type.\n * The node definitions here are only the ones of \"composite\" attributes.\n */\nexport const ValuePropsByType: { [key in NodeDefType]: { [key: string]: string } | null } = {\n [NodeDefType.boolean]: null,\n [NodeDefType.code]: ValuePropsCode,\n [NodeDefType.coordinate]: ValuePropsCoordinate,\n [NodeDefType.date]: ValuePropsDate,\n [NodeDefType.decimal]: null,\n [NodeDefType.geo]: null,\n [NodeDefType.entity]: null,\n [NodeDefType.file]: ValuePropsFile,\n [NodeDefType.integer]: null,\n [NodeDefType.taxon]: ValuePropsTaxon,\n [NodeDefType.time]: ValuePropsTime,\n [NodeDefType.text]: null,\n // layout elements\n [NodeDefType.formHeader]: null,\n}\n\nconst getValuePropRaw = (params: { value: any; prop: string; defaultValue?: any }): any => {\n const { value, prop, defaultValue } = params\n const valueProp = value?.[prop]\n return valueProp === undefined ? defaultValue : valueProp\n}\n\nconst getNodeValuePropRaw = (params: { node: ArenaRecordNode; prop: string; defaultValue?: any }): any => {\n const { node, prop, defaultValue } = params\n return getValuePropRaw({ value: node.value, prop, defaultValue })\n}\n\n// Code\nconst newCodeValue = (params: { itemUuid: string }) => {\n const { itemUuid } = params\n return { [ValuePropsCode[ValuePropsCode.itemUuid]]: itemUuid }\n}\n\nconst getItemUuid = (node: ArenaRecordNode): string | undefined =>\n getNodeValuePropRaw({ node, prop: ValuePropsCode[ValuePropsCode.itemUuid] })\n\nconst getValueCode = (value: any): string | undefined =>\n getValuePropRaw({ value, prop: ValuePropsCode[ValuePropsCode.code] })\n\nconst getValueItemUuid = (value: any): string | undefined =>\n getValuePropRaw({ value, prop: ValuePropsCode[ValuePropsCode.itemUuid] })\n\nconst getDateTimePart = (params: { node: ArenaRecordNode; index: number; separator: string }): number => {\n const { node, index, separator } = params\n return Number((node.value ?? '').split(separator)[index]?.trim())\n}\n\n// Date\nconst getDatePart =\n (index: number) =>\n (node: ArenaRecordNode): number =>\n getDateTimePart({ node, index, separator: '-' })\nconst getDateYear = getDatePart(0)\nconst getDateMonth = getDatePart(1)\nconst getDateDay = getDatePart(2)\n\nconst _datePropGetters: { [key in ValuePropsDate]: (node: ArenaRecordNode) => number } = {\n [ValuePropsDate.day]: getDateDay,\n [ValuePropsDate.month]: getDateMonth,\n [ValuePropsDate.year]: getDateYear,\n}\n\n// File\nconst getFileName = (node: ArenaRecordNode): string | undefined =>\n getNodeValuePropRaw({ node, prop: ValuePropsFile.fileName })\nconst getFileNameCalculated = (node: ArenaRecordNode): string | undefined =>\n getNodeValuePropRaw({ node, prop: ValuePropsFile.fileNameCalculated })\nconst getFileSize = (node: ArenaRecordNode): string | undefined =>\n getNodeValuePropRaw({ node, prop: ValuePropsFile.fileSize })\nconst getFileUuid = (node: ArenaRecordNode): string | undefined =>\n getNodeValuePropRaw({ node, prop: ValuePropsFile.fileUuid })\n\n// Taxon\nconst getTaxonUuid = (node: ArenaRecordNode): string | undefined =>\n getNodeValuePropRaw({ node, prop: ValuePropsTaxon[ValuePropsTaxon.taxonUuid] })\nconst getValueTaxonUuid = (value: any): string | undefined =>\n getValuePropRaw({ value, prop: ValuePropsTaxon[ValuePropsTaxon.taxonUuid] })\nconst getScientificName = (node: ArenaRecordNode): string | undefined =>\n getNodeValuePropRaw({ node, prop: ValuePropsTaxon[ValuePropsTaxon.scientificName] })\nconst getValueScientificName = (value: any): string | undefined =>\n getValuePropRaw({ value, prop: ValuePropsTaxon[ValuePropsTaxon.scientificName] })\nconst getVernacularName = (node: ArenaRecordNode): string | undefined =>\n getNodeValuePropRaw({ node, prop: ValuePropsTaxon[ValuePropsTaxon.vernacularName] })\nconst getValueVernacularName = (value: any): string | undefined =>\n getValuePropRaw({ value, prop: ValuePropsTaxon[ValuePropsTaxon.vernacularName] })\nconst getVernacularNameUuid = (node: ArenaRecordNode): string | undefined =>\n getNodeValuePropRaw({ node, prop: ValuePropsTaxon[ValuePropsTaxon.vernacularNameUuid] })\nconst getValueVernacularNameUuid = (value: any): string | undefined =>\n getValuePropRaw({ value, prop: ValuePropsTaxon[ValuePropsTaxon.vernacularNameUuid] })\n\n// Time\nconst _getTimePart =\n (index: number) =>\n (node: ArenaRecordNode): number =>\n getDateTimePart({ node, index, separator: ':' })\nconst getTimeHour = _getTimePart(0)\nconst getTimeMinute = _getTimePart(1)\nconst getTimeSeconds = (node: ArenaRecordNode): number => {\n const parts = (node.value ?? '').split(':')\n return parts.length > 2 ? _getTimePart(2)(node) : 0\n}\n\nconst _timePropGetters: { [key in ValuePropsTime]: any } = {\n [ValuePropsTime.hour]: getTimeHour,\n [ValuePropsTime.minute]: getTimeMinute,\n [ValuePropsTime.seconds]: getTimeSeconds,\n}\n\nconst _valuePropGetters: { [key in NodeDefType]?: (prop: string) => (node: ArenaRecordNode) => number } = {\n [NodeDefType.date]: (prop: string) => _datePropGetters[ValuePropsDate[prop as keyof typeof ValuePropsDate]],\n [NodeDefType.time]: (prop: string) => _timePropGetters[ValuePropsTime[prop as keyof typeof ValuePropsTime]],\n}\n\nconst isValueProp = (params: { nodeDef: NodeDef<NodeDefType, NodeDefProps>; prop: string }): boolean => {\n const { nodeDef, prop } = params\n return Object.values(ValuePropsByType[nodeDef.type] ?? {}).includes(prop)\n}\n\nconst getNodeValueProp = (params: {\n nodeDef: NodeDef<NodeDefType, NodeDefProps>\n node: ArenaRecordNode\n prop: string\n}): any => {\n const { nodeDef, node, prop } = params\n const propGetter: any = _valuePropGetters[nodeDef.type]\n return propGetter ? propGetter(prop)(node) : getNodeValuePropRaw({ node, prop })\n}\n\n// value compare\n\nconst singlePropValueEqualComparator = (params: NodeValuesCompareParams): boolean => {\n const { value, valueSearch } = params\n return value === valueSearch || String(value) === String(valueSearch)\n}\n\nconst extractCategoryItemUuidFromValue = (params: NodeValuesCompareParams): string | undefined => {\n const { survey, nodeDef, record, parentNode, value } = params\n const valueItemUuid = getValueItemUuid(value)\n if (valueItemUuid) {\n return valueItemUuid\n }\n // find itemUuid by code\n const valueCode = getValueCode(value)\n if (Objects.isEmpty(valueCode)) return undefined\n\n const nodeDefCode = nodeDef as NodeDefCode\n const code = valueCode!\n const categoryUuid = nodeDefCode.props?.categoryUuid\n const levelIndex = Surveys.getNodeDefCategoryLevelIndex({ survey, nodeDef: nodeDefCode })\n if (levelIndex === 0) {\n return Surveys.getCategoryItemUuidByCode({ survey, categoryUuid, parentItemUuid: undefined, code })\n }\n if (!record || !parentNode) return undefined\n\n const parentCodeAttribute = Records.getParentCodeAttribute(parentNode, nodeDef as NodeDefCode)(record)\n if (!parentCodeAttribute) return undefined\n\n const parentItemUuid = getItemUuid(parentCodeAttribute)\n if (!parentItemUuid) return undefined\n\n return Surveys.getCategoryItemUuidByCode({\n survey,\n categoryUuid,\n parentItemUuid,\n code,\n })\n}\n\nconst extractCategoryItemCodeFromValue = (params: { survey: Survey; value: any }): string | undefined => {\n const { survey, value } = params\n const itemUuid = getValueItemUuid(value)\n if (itemUuid) {\n const item = Surveys.getCategoryItemByUuid({ survey, itemUuid })\n return item ? CategoryItems.getCode(item) : undefined\n }\n return getValueCode(value)\n}\n\nconst dateTimeComparator =\n (params: { formatsSource: DateFormats[]; formatTo: DateFormats }) =>\n (paramsComparator: NodeValuesCompareParams): boolean => {\n const { formatsSource, formatTo } = params\n const { value, valueSearch } = paramsComparator\n const toDateTime = (val: any) => {\n if (val instanceof Date) {\n return Dates.format(val, formatTo)\n }\n const formatFrom = formatsSource.find((format) => Dates.isValidDateInFormat(val, format))\n return formatFrom ? Dates.convertDate({ dateStr: val, formatFrom, formatTo }) : null\n }\n const dateTime = toDateTime(value)\n const dateTimeSearch = toDateTime(valueSearch)\n return !!dateTime && !!dateTimeSearch && dateTime === dateTimeSearch\n }\n\nconst valueComparatorByNodeDefType: { [key in NodeDefType]?: (params: NodeValuesCompareParams) => boolean } = {\n [NodeDefType.boolean]: singlePropValueEqualComparator,\n [NodeDefType.code]: (params: NodeValuesCompareParams): boolean => {\n const { survey, record, value, valueSearch, strict } = params\n if (!strict || !record) {\n // compare just codes (record not available, tricky to find the \"correct\" category item without knowing its parent item)\n const code = extractCategoryItemCodeFromValue({ survey, value })\n const codeSearch = extractCategoryItemCodeFromValue({ survey, value: valueSearch })\n return !!code && !!codeSearch && code === codeSearch\n }\n const itemUuid = extractCategoryItemUuidFromValue(params)\n const itemUuidSearch = extractCategoryItemUuidFromValue({\n ...params,\n value: valueSearch,\n })\n return !!itemUuidSearch && !!itemUuid && itemUuidSearch === itemUuid\n },\n [NodeDefType.coordinate]: (params: NodeValuesCompareParams) => {\n const { value, valueSearch } = params\n return Objects.isEqual(value, valueSearch)\n },\n [NodeDefType.date]: dateTimeComparator({\n formatsSource: [DateFormats.dateDisplay, DateFormats.dateStorage],\n formatTo: DateFormats.dateStorage,\n }),\n [NodeDefType.decimal]: singlePropValueEqualComparator,\n [NodeDefType.integer]: singlePropValueEqualComparator,\n [NodeDefType.taxon]: ({ value, valueSearch }) => {\n if (value === valueSearch) return true\n if (!value) return false\n if (!valueSearch) return false\n return value[ValuePropsTaxon.taxonUuid] === valueSearch[ValuePropsTaxon.taxonUuid]\n },\n [NodeDefType.text]: singlePropValueEqualComparator,\n [NodeDefType.time]: dateTimeComparator({\n formatsSource: [DateFormats.timeStorage, DateFormats.timeWithSeconds],\n formatTo: DateFormats.timeWithSeconds,\n }),\n}\n\ninterface NodeValuesCompareParams {\n survey: Survey\n nodeDef: NodeDef<NodeDefType, any>\n value: any\n valueSearch: any\n record?: ArenaRecord\n parentNode?: ArenaRecordNode\n strict?: boolean\n}\n\nconst isValueEqual = (params: NodeValuesCompareParams): boolean => {\n const { survey, nodeDef, value, valueSearch, record = undefined, parentNode = undefined, strict = false } = params\n if (value === valueSearch) return true\n if (Objects.isEmpty(value) || Objects.isEmpty(valueSearch)) return false\n\n const valueComparator = valueComparatorByNodeDefType[nodeDef.type]\n if (!valueComparator) {\n return Objects.isEqual(value, valueSearch)\n }\n return valueComparator({ survey, nodeDef, record, parentNode, value, valueSearch, strict })\n}\n\nconst getValueAsPoint = (params: { survey: Survey; node: ArenaRecordNode }): Point | null => {\n const { survey, node } = params\n const nodeValue = node?.value\n\n if (!nodeValue) return null\n\n const nodeDef = Surveys.getNodeDefByUuid({ survey, uuid: node.nodeDefUuid })\n if (nodeDef.type === NodeDefType.coordinate) {\n const { x, y, srs } = nodeValue as NodeValueCoordinate\n\n const srsObject = Surveys.getSRSByCode(srs)(survey)\n if (!srsObject) return null\n\n return PointFactory.createInstance({ srs, x, y })\n }\n return null\n}\n\nexport const NodeValues = {\n ValuePropsByType,\n ValuePropsCode,\n ValuePropsCoordinate,\n ValuePropsDate,\n ValuePropsFile,\n ValuePropsTaxon,\n ValuePropsTime,\n\n getNodeValueProp,\n isValueProp,\n\n // coordinate\n getValueAsPoint,\n\n // code\n newCodeValue,\n getItemUuid,\n getValueCode,\n getValueItemUuid,\n\n // date\n getDateYear,\n getDateMonth,\n getDateDay,\n\n // file\n getFileName,\n getFileNameCalculated,\n getFileSize,\n getFileUuid,\n\n // taxon\n getTaxonUuid,\n getValueTaxonUuid,\n getScientificName,\n getValueScientificName,\n getVernacularName,\n getValueVernacularName,\n getVernacularNameUuid,\n getValueVernacularNameUuid,\n\n // time\n getTimeHour,\n getTimeMinute,\n getTimeSeconds,\n\n // utils\n isValueEqual,\n}\n"]}
1
+ {"version":3,"file":"nodeValues.js","sourceRoot":"","sources":["../../src/node/nodeValues.ts"],"names":[],"mappings":"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","sourcesContent":["import { CategoryItems } from '../category'\nimport { Point, PointFactory } from '../geo'\nimport { NodeDef, NodeDefCode, NodeDefProps, NodeDefType } from '../nodeDef'\nimport { ArenaRecord, Records } from '../record'\nimport { Survey, Surveys } from '../survey'\nimport { DateFormats, Dates, Objects } from '../utils'\nimport { ArenaRecordNode } from './node'\nimport { NodeValueCoordinate } from './nodeValue/coordinate'\nimport {\n ValuePropsCode,\n ValuePropsCoordinate,\n ValuePropsDate,\n ValuePropsFile,\n ValuePropsTaxon,\n ValuePropsTime,\n} from './nodeValueProps'\n\n/**\n * Props of node value indexed by node def type.\n * The node definitions here are only the ones of \"composite\" attributes.\n */\nexport const ValuePropsByType: { [key in NodeDefType]: { [key: string]: string } | null } = {\n [NodeDefType.boolean]: null,\n [NodeDefType.code]: ValuePropsCode,\n [NodeDefType.coordinate]: ValuePropsCoordinate,\n [NodeDefType.date]: ValuePropsDate,\n [NodeDefType.decimal]: null,\n [NodeDefType.geo]: null,\n [NodeDefType.entity]: null,\n [NodeDefType.file]: ValuePropsFile,\n [NodeDefType.integer]: null,\n [NodeDefType.taxon]: ValuePropsTaxon,\n [NodeDefType.time]: ValuePropsTime,\n [NodeDefType.text]: null,\n // layout elements\n [NodeDefType.formHeader]: null,\n}\n\nconst getValuePropRaw = (params: { value: any; prop: string; defaultValue?: any }): any => {\n const { value, prop, defaultValue } = params\n const valueProp = value?.[prop]\n return valueProp === undefined ? defaultValue : valueProp\n}\n\nconst getNodeValuePropRaw = (params: { node: ArenaRecordNode; prop: string; defaultValue?: any }): any => {\n const { node, prop, defaultValue } = params\n return getValuePropRaw({ value: node.value, prop, defaultValue })\n}\n\n// Code\nconst newCodeValue = (params: { itemUuid: string }) => {\n const { itemUuid } = params\n return { [ValuePropsCode[ValuePropsCode.itemUuid]]: itemUuid }\n}\n\nconst getItemUuid = (node: ArenaRecordNode): string | undefined =>\n getNodeValuePropRaw({ node, prop: ValuePropsCode[ValuePropsCode.itemUuid] })\n\nconst getValueCode = (value: any): string | undefined =>\n getValuePropRaw({ value, prop: ValuePropsCode[ValuePropsCode.code] })\n\nconst getValueItemUuid = (value: any): string | undefined =>\n getValuePropRaw({ value, prop: ValuePropsCode[ValuePropsCode.itemUuid] })\n\nconst getDateTimePart = (params: { node: ArenaRecordNode; index: number; separator: string }): number => {\n const { node, index, separator } = params\n return Number((node.value ?? '').split(separator)[index]?.trim())\n}\n\n// Date\nconst getDatePart =\n (index: number) =>\n (node: ArenaRecordNode): number =>\n getDateTimePart({ node, index, separator: '-' })\nconst getDateYear = getDatePart(0)\nconst getDateMonth = getDatePart(1)\nconst getDateDay = getDatePart(2)\n\nconst _datePropGetters: { [key in ValuePropsDate]: (node: ArenaRecordNode) => number } = {\n [ValuePropsDate.day]: getDateDay,\n [ValuePropsDate.month]: getDateMonth,\n [ValuePropsDate.year]: getDateYear,\n}\n\n// File\nconst getFileName = (node: ArenaRecordNode): string | undefined =>\n getNodeValuePropRaw({ node, prop: ValuePropsFile.fileName })\nconst getFileNameCalculated = (node: ArenaRecordNode): string | undefined =>\n getNodeValuePropRaw({ node, prop: ValuePropsFile.fileNameCalculated })\nconst getFileSize = (node: ArenaRecordNode): string | undefined =>\n getNodeValuePropRaw({ node, prop: ValuePropsFile.fileSize })\nconst getFileUuid = (node: ArenaRecordNode): string | undefined =>\n getNodeValuePropRaw({ node, prop: ValuePropsFile.fileUuid })\n\n// Taxon\nconst getTaxonUuid = (node: ArenaRecordNode): string | undefined =>\n getNodeValuePropRaw({ node, prop: ValuePropsTaxon[ValuePropsTaxon.taxonUuid] })\nconst getValueTaxonUuid = (value: any): string | undefined =>\n getValuePropRaw({ value, prop: ValuePropsTaxon[ValuePropsTaxon.taxonUuid] })\nconst getScientificName = (node: ArenaRecordNode): string | undefined =>\n getNodeValuePropRaw({ node, prop: ValuePropsTaxon[ValuePropsTaxon.scientificName] })\nconst getValueScientificName = (value: any): string | undefined =>\n getValuePropRaw({ value, prop: ValuePropsTaxon[ValuePropsTaxon.scientificName] })\nconst getVernacularName = (node: ArenaRecordNode): string | undefined =>\n getNodeValuePropRaw({ node, prop: ValuePropsTaxon[ValuePropsTaxon.vernacularName] })\nconst getValueVernacularName = (value: any): string | undefined =>\n getValuePropRaw({ value, prop: ValuePropsTaxon[ValuePropsTaxon.vernacularName] })\nconst getVernacularNameUuid = (node: ArenaRecordNode): string | undefined =>\n getNodeValuePropRaw({ node, prop: ValuePropsTaxon[ValuePropsTaxon.vernacularNameUuid] })\nconst getValueVernacularNameUuid = (value: any): string | undefined =>\n getValuePropRaw({ value, prop: ValuePropsTaxon[ValuePropsTaxon.vernacularNameUuid] })\n\n// Time\nconst _getTimePart =\n (index: number) =>\n (node: ArenaRecordNode): number =>\n getDateTimePart({ node, index, separator: ':' })\nconst getTimeHour = _getTimePart(0)\nconst getTimeMinute = _getTimePart(1)\nconst getTimeSeconds = (node: ArenaRecordNode): number => {\n const parts = (node.value ?? '').split(':')\n return parts.length > 2 ? _getTimePart(2)(node) : 0\n}\n\nconst _timePropGetters: { [key in ValuePropsTime]: any } = {\n [ValuePropsTime.hour]: getTimeHour,\n [ValuePropsTime.minute]: getTimeMinute,\n [ValuePropsTime.seconds]: getTimeSeconds,\n}\n\nconst _valuePropGetters: { [key in NodeDefType]?: (prop: string) => (node: ArenaRecordNode) => number } = {\n [NodeDefType.date]: (prop: string) => _datePropGetters[ValuePropsDate[prop as keyof typeof ValuePropsDate]],\n [NodeDefType.time]: (prop: string) => _timePropGetters[ValuePropsTime[prop as keyof typeof ValuePropsTime]],\n}\n\nconst isValueProp = (params: { nodeDef: NodeDef<NodeDefType, NodeDefProps>; prop: string }): boolean => {\n const { nodeDef, prop } = params\n return Object.values(ValuePropsByType[nodeDef.type] ?? {}).includes(prop)\n}\n\nconst getNodeValueProp = (params: {\n nodeDef: NodeDef<NodeDefType, NodeDefProps>\n node: ArenaRecordNode\n prop: string\n}): any => {\n const { nodeDef, node, prop } = params\n const propGetter: any = _valuePropGetters[nodeDef.type]\n return propGetter ? propGetter(prop)(node) : getNodeValuePropRaw({ node, prop })\n}\n\n// value compare\n\nconst singlePropValueEqualComparator = (params: NodeValuesCompareParams): boolean => {\n const { value, valueSearch } = params\n return value === valueSearch || String(value) === String(valueSearch)\n}\n\nconst extractCategoryItemUuidFromValue = (params: NodeValuesCompareParams): string | undefined => {\n const { survey, nodeDef, record, parentNode, value } = params\n const valueItemUuid = getValueItemUuid(value)\n if (valueItemUuid) {\n return valueItemUuid\n }\n // find itemUuid by code\n const valueCode = getValueCode(value)\n if (Objects.isEmpty(valueCode)) return undefined\n\n const nodeDefCode = nodeDef as NodeDefCode\n const code = valueCode!\n const categoryUuid = nodeDefCode.props?.categoryUuid\n const levelIndex = Surveys.getNodeDefCategoryLevelIndex({ survey, nodeDef: nodeDefCode })\n if (levelIndex === 0) {\n return Surveys.getCategoryItemUuidByCode({ survey, categoryUuid, parentItemUuid: undefined, code })\n }\n if (!record || !parentNode) return undefined\n\n const parentCodeAttribute = Records.getParentCodeAttribute(parentNode, nodeDef as NodeDefCode)(record)\n if (!parentCodeAttribute) return undefined\n\n const parentItemUuid = getItemUuid(parentCodeAttribute)\n if (!parentItemUuid) return undefined\n\n return Surveys.getCategoryItemUuidByCode({\n survey,\n categoryUuid,\n parentItemUuid,\n code,\n })\n}\n\nconst extractCategoryItemCodeFromValue = (params: { survey: Survey; value: any }): string | undefined => {\n const { survey, value } = params\n const itemUuid = getValueItemUuid(value)\n if (itemUuid) {\n const item = Surveys.getCategoryItemByUuid({ survey, itemUuid })\n return item ? CategoryItems.getCode(item) : undefined\n }\n return getValueCode(value)\n}\n\ntype DateTimeFormats = { formatsSource: DateFormats[]; formatTo: DateFormats }\n\nconst dateFormats: DateTimeFormats = {\n formatsSource: [DateFormats.dateDisplay, DateFormats.dateStorage],\n formatTo: DateFormats.dateStorage,\n}\n\nconst timeFormats: DateTimeFormats = {\n formatsSource: [DateFormats.timeStorage, DateFormats.timeWithSeconds],\n formatTo: DateFormats.timeWithSeconds,\n}\n\nconst toDateTime = (params: DateTimeFormats & { value: any }) => {\n const { formatsSource, formatTo, value } = params\n if (value instanceof Date) {\n return Dates.format(value, formatTo)\n }\n const formatFrom = formatsSource.find((format) => Dates.isValidDateInFormat(value, format))\n return formatFrom ? Dates.convertDate({ dateStr: value, formatFrom, formatTo }) : null\n}\n\nconst dateTimeComparator =\n (formats: DateTimeFormats) =>\n (paramsComparator: NodeValuesCompareParams): boolean => {\n const { value, valueSearch } = paramsComparator\n const dateTime = toDateTime({ ...formats, value })\n const dateTimeSearch = toDateTime({ ...formats, value: valueSearch })\n return !!dateTime && !!dateTimeSearch && dateTime === dateTimeSearch\n }\n\nconst valueComparatorByNodeDefType: { [key in NodeDefType]?: (params: NodeValuesCompareParams) => boolean } = {\n [NodeDefType.boolean]: singlePropValueEqualComparator,\n [NodeDefType.code]: (params: NodeValuesCompareParams): boolean => {\n const { survey, record, value, valueSearch, strict } = params\n if (!strict || !record) {\n // compare just codes (record not available, tricky to find the \"correct\" category item without knowing its parent item)\n const code = extractCategoryItemCodeFromValue({ survey, value })\n const codeSearch = extractCategoryItemCodeFromValue({ survey, value: valueSearch })\n return !!code && !!codeSearch && code === codeSearch\n }\n const itemUuid = extractCategoryItemUuidFromValue(params)\n const itemUuidSearch = extractCategoryItemUuidFromValue({\n ...params,\n value: valueSearch,\n })\n return !!itemUuidSearch && !!itemUuid && itemUuidSearch === itemUuid\n },\n [NodeDefType.coordinate]: (params: NodeValuesCompareParams) => {\n const { value, valueSearch } = params\n return Objects.isEqual(value, valueSearch)\n },\n [NodeDefType.date]: dateTimeComparator(dateFormats),\n [NodeDefType.decimal]: singlePropValueEqualComparator,\n [NodeDefType.integer]: singlePropValueEqualComparator,\n [NodeDefType.taxon]: ({ value, valueSearch }) => {\n if (value === valueSearch) return true\n if (!value) return false\n if (!valueSearch) return false\n return value[ValuePropsTaxon.taxonUuid] === valueSearch[ValuePropsTaxon.taxonUuid]\n },\n [NodeDefType.text]: singlePropValueEqualComparator,\n [NodeDefType.time]: dateTimeComparator(timeFormats),\n}\n\ninterface NodeValuesCompareParams {\n survey: Survey\n nodeDef: NodeDef<NodeDefType, any>\n value: any\n valueSearch: any\n record?: ArenaRecord\n parentNode?: ArenaRecordNode\n strict?: boolean\n}\n\nconst isValueEqual = (params: NodeValuesCompareParams): boolean => {\n const { survey, nodeDef, value, valueSearch, record = undefined, parentNode = undefined, strict = false } = params\n if (value === valueSearch) return true\n if (Objects.isEmpty(value) || Objects.isEmpty(valueSearch)) return false\n\n const valueComparator = valueComparatorByNodeDefType[nodeDef.type]\n if (!valueComparator) {\n return Objects.isEqual(value, valueSearch)\n }\n return valueComparator({ survey, nodeDef, record, parentNode, value, valueSearch, strict })\n}\n\nconst primitiveTypes = new Set(['string', 'number', 'boolean', 'undefined', 'bigint'])\n\n// token of a primitive value, equal for strictly equal (===) values only (distinguishes \"1\" from 1);\n// undefined for non primitive values and NaN (not equal to itself)\nconst toPrimitiveToken = (value: any): string | undefined =>\n (value === null || primitiveTypes.has(typeof value)) && !Number.isNaN(value)\n ? `${typeof value}:${String(value)}`\n : undefined\n\nconst dateTimeKeyExtractor =\n (formats: DateTimeFormats) =>\n ({ value }: { value: any }): string | undefined =>\n toDateTime({ ...formats, value }) || undefined\n\nconst valueEqualityKeyByNodeDefType: {\n [key in NodeDefType]?: (params: { survey: Survey; value: any }) => string | undefined\n} = {\n [NodeDefType.boolean]: ({ value }) => String(value),\n [NodeDefType.code]: ({ survey, value }) => {\n const code = extractCategoryItemCodeFromValue({ survey, value })\n return code ? toPrimitiveToken(code) : undefined\n },\n [NodeDefType.date]: dateTimeKeyExtractor(dateFormats),\n [NodeDefType.decimal]: ({ value }) => String(value),\n [NodeDefType.integer]: ({ value }) => String(value),\n [NodeDefType.taxon]: ({ value }) => toPrimitiveToken(value[ValuePropsTaxon.taxonUuid]),\n [NodeDefType.text]: ({ value }) => String(value),\n [NodeDefType.time]: dateTimeKeyExtractor(timeFormats),\n}\n\n/**\n * Returns a key such that two values have the same key if and only if they are equal according to isValueEqual\n * (not strict, without record), or undefined if such a key cannot be built for the value (e.g. NaN, empty objects, code values\n * without code, invalid dates, coordinates): such values must be compared with isValueEqual.\n * Not exported in NodeValues: internal, used to look up duplicate values without comparing every pair of values.\n */\nexport const getValueEqualityKey = (params: {\n survey: Survey\n nodeDef: NodeDef<NodeDefType, any>\n value: any\n}): string | undefined => {\n const { survey, nodeDef, value } = params\n if (Objects.isEmpty(value)) {\n // empty values are equal only if identical (===)\n const token = toPrimitiveToken(value)\n return token === undefined ? undefined : `empty:${token}`\n }\n const keyExtractor = valueEqualityKeyByNodeDefType[nodeDef.type]\n const key = keyExtractor?.({ survey, value })\n return key === undefined ? undefined : `value:${key}`\n}\n\nconst getValueAsPoint = (params: { survey: Survey; node: ArenaRecordNode }): Point | null => {\n const { survey, node } = params\n const nodeValue = node?.value\n\n if (!nodeValue) return null\n\n const nodeDef = Surveys.getNodeDefByUuid({ survey, uuid: node.nodeDefUuid })\n if (nodeDef.type === NodeDefType.coordinate) {\n const { x, y, srs } = nodeValue as NodeValueCoordinate\n\n const srsObject = Surveys.getSRSByCode(srs)(survey)\n if (!srsObject) return null\n\n return PointFactory.createInstance({ srs, x, y })\n }\n return null\n}\n\nexport const NodeValues = {\n ValuePropsByType,\n ValuePropsCode,\n ValuePropsCoordinate,\n ValuePropsDate,\n ValuePropsFile,\n ValuePropsTaxon,\n ValuePropsTime,\n\n getNodeValueProp,\n isValueProp,\n\n // coordinate\n getValueAsPoint,\n\n // code\n newCodeValue,\n getItemUuid,\n getValueCode,\n getValueItemUuid,\n\n // date\n getDateYear,\n getDateMonth,\n getDateDay,\n\n // file\n getFileName,\n getFileNameCalculated,\n getFileSize,\n getFileUuid,\n\n // taxon\n getTaxonUuid,\n getValueTaxonUuid,\n getScientificName,\n getValueScientificName,\n getVernacularName,\n getValueVernacularName,\n getVernacularNameUuid,\n getValueVernacularNameUuid,\n\n // time\n getTimeHour,\n getTimeMinute,\n getTimeSeconds,\n\n // utils\n isValueEqual,\n}\n"]}
@@ -4,13 +4,25 @@ import { ArenaRecordNode } from '../../node';
4
4
  import { ValidationResult } from '../../validation';
5
5
  import { Survey } from '../../survey';
6
6
  import { Dictionary } from '../../common';
7
+ /**
8
+ * Sibling entities (same parent and entity def) indexed by the equality key of their key values:
9
+ * entities with the same equality key are duplicates; entities whose equality key cannot be built (see getValueEqualityKey)
10
+ * are compared with every sibling.
11
+ */
12
+ type SiblingEntitiesKeysIndex = {
13
+ entitiesByEqualityKey: Map<string, ArenaRecordNode[]>;
14
+ entitiesWithoutEqualityKey: ArenaRecordNode[];
15
+ };
7
16
  /**
8
17
  * Cache of entity key values (and key defs by entity def) used during a single validation run (the record doesn't change):
9
- * without it, validating the keys of S sibling entities would compute the key values of every sibling S times.
18
+ * without it, validating the keys of S sibling entities would compute the key values of every sibling S times
19
+ * and compare them S times with the key values of every sibling.
10
20
  */
11
21
  export type EntityKeysCache = {
12
22
  keyDefsByEntityDefUuid: Map<string, NodeDef<NodeDefType, NodeDefProps>[]>;
13
23
  keyValuesByEntityUuid: Map<string, Dictionary<any>>;
24
+ equalityKeyByEntityUuid: Map<string, string | undefined>;
25
+ siblingEntitiesKeysIndexByParentAndDefUuid: Map<string, SiblingEntitiesKeysIndex>;
14
26
  };
15
27
  export declare const createEntityKeysCache: () => EntityKeysCache;
16
28
  declare const validateAttributeKey: (params: {
@@ -4,12 +4,15 @@ exports.AttributeKeyValidator = exports.createEntityKeysCache = void 0;
4
4
  const records_1 = require("../records");
5
5
  const nodeDef_1 = require("../../nodeDef");
6
6
  const node_1 = require("../../node");
7
+ const nodeValues_1 = require("../../node/nodeValues");
7
8
  const validation_1 = require("../../validation");
8
9
  const survey_1 = require("../../survey");
9
10
  const utils_1 = require("../../utils");
10
11
  const createEntityKeysCache = () => ({
11
12
  keyDefsByEntityDefUuid: new Map(),
12
13
  keyValuesByEntityUuid: new Map(),
14
+ equalityKeyByEntityUuid: new Map(),
15
+ siblingEntitiesKeysIndexByParentAndDefUuid: new Map(),
13
16
  });
14
17
  exports.createEntityKeysCache = createEntityKeysCache;
15
18
  const _getKeyDefs = (params) => {
@@ -30,41 +33,95 @@ const _getKeyValuesByDefUuid = (params) => {
30
33
  }
31
34
  return keyValues;
32
35
  };
36
+ const _getEqualityKey = (params) => {
37
+ const { survey, entity, keyDefs, keyValuesByDefUuid, cache } = params;
38
+ if (cache.equalityKeyByEntityUuid.has(entity.uuid))
39
+ return cache.equalityKeyByEntityUuid.get(entity.uuid);
40
+ const keyParts = [];
41
+ for (const keyDef of keyDefs) {
42
+ const keyPart = (0, nodeValues_1.getValueEqualityKey)({ survey, nodeDef: keyDef, value: keyValuesByDefUuid[keyDef.uuid] });
43
+ if (keyPart === undefined) {
44
+ keyParts.length = 0;
45
+ break;
46
+ }
47
+ keyParts.push(keyPart);
48
+ }
49
+ const equalityKey = keyParts.length > 0 ? JSON.stringify(keyParts) : undefined;
50
+ cache.equalityKeyByEntityUuid.set(entity.uuid, equalityKey);
51
+ return equalityKey;
52
+ };
53
+ const _areKeyValuesEqual = (params) => {
54
+ const { survey, keyDefs, keyValuesByDefUuid, siblingKeyValuesByDefUuid } = params;
55
+ return keyDefs.every((keyDef) => node_1.NodeValues.isValueEqual({
56
+ survey,
57
+ nodeDef: keyDef,
58
+ value: keyValuesByDefUuid[keyDef.uuid],
59
+ valueSearch: siblingKeyValuesByDefUuid[keyDef.uuid],
60
+ }));
61
+ };
62
+ const _getSiblingEntitiesKeysIndex = (params) => {
63
+ const { survey, record, nodeParent, entityDefUuid, keyDefs, cache } = params;
64
+ const indexKey = `${nodeParent.uuid}_${entityDefUuid}`;
65
+ let index = cache.siblingEntitiesKeysIndexByParentAndDefUuid.get(indexKey);
66
+ if (!index) {
67
+ index = { entitiesByEqualityKey: new Map(), entitiesWithoutEqualityKey: [] };
68
+ for (const siblingEntity of records_1.Records.getChildren(nodeParent, entityDefUuid)(record)) {
69
+ const keyValuesByDefUuid = _getKeyValuesByDefUuid({ survey, record, entity: siblingEntity, keyDefs, cache });
70
+ const equalityKey = _getEqualityKey({ survey, entity: siblingEntity, keyDefs, keyValuesByDefUuid, cache });
71
+ if (equalityKey === undefined) {
72
+ index.entitiesWithoutEqualityKey.push(siblingEntity);
73
+ }
74
+ else {
75
+ const entitiesWithSameKey = index.entitiesByEqualityKey.get(equalityKey);
76
+ if (entitiesWithSameKey) {
77
+ entitiesWithSameKey.push(siblingEntity);
78
+ }
79
+ else {
80
+ index.entitiesByEqualityKey.set(equalityKey, [siblingEntity]);
81
+ }
82
+ }
83
+ }
84
+ cache.siblingEntitiesKeysIndexByParentAndDefUuid.set(indexKey, index);
85
+ }
86
+ return index;
87
+ };
33
88
  const _isEntityDuplicate = (params) => {
89
+ var _a;
34
90
  const { survey, entity, record, entityKeysCache: cache } = params;
35
- // 1. get sibling entities
36
91
  const nodeParent = records_1.Records.getParent(entity)(record);
37
92
  if (!nodeParent)
38
93
  return false;
39
- const siblingEntities = records_1.Records.getChildren(nodeParent, entity.nodeDefUuid)(record).filter((node) => !node_1.Nodes.areEqual(entity, node));
40
- // 2. get key values
41
94
  const entityDef = survey_1.Surveys.getNodeDefByUuid({ survey, uuid: entity.nodeDefUuid });
42
95
  const keyDefs = _getKeyDefs({ survey, entityDef, cache });
43
- if (utils_1.Objects.isEmpty(siblingEntities) || utils_1.Objects.isEmpty(keyDefs)) {
96
+ if (utils_1.Objects.isEmpty(keyDefs))
44
97
  return false;
45
- }
46
98
  const keyValuesByDefUuid = _getKeyValuesByDefUuid({ survey, record, entity, keyDefs, cache });
47
99
  // entities with all key values empty are never duplicates (they are reported as missing values)
48
- return (!Object.values(keyValuesByDefUuid).every(utils_1.Objects.isEmpty) &&
49
- siblingEntities.some((sibilingEntity) => {
50
- const siblingKeyValuesByDefUuid = _getKeyValuesByDefUuid({
51
- survey,
52
- record,
53
- entity: sibilingEntity,
54
- keyDefs,
55
- cache,
56
- });
57
- return keyDefs.every((keyDef) => {
58
- const keyValue = keyValuesByDefUuid[keyDef.uuid];
59
- const siblingKeyValue = siblingKeyValuesByDefUuid[keyDef.uuid];
60
- return node_1.NodeValues.isValueEqual({
61
- survey,
62
- nodeDef: keyDef,
63
- value: keyValue,
64
- valueSearch: siblingKeyValue,
65
- });
66
- });
67
- }));
100
+ if (Object.values(keyValuesByDefUuid).every(utils_1.Objects.isEmpty))
101
+ return false;
102
+ const isDuplicateOf = (siblingEntity) => !node_1.Nodes.areEqual(entity, siblingEntity) &&
103
+ _areKeyValuesEqual({
104
+ survey,
105
+ keyDefs,
106
+ keyValuesByDefUuid,
107
+ siblingKeyValuesByDefUuid: _getKeyValuesByDefUuid({ survey, record, entity: siblingEntity, keyDefs, cache }),
108
+ });
109
+ const equalityKey = cache ? _getEqualityKey({ survey, entity, keyDefs, keyValuesByDefUuid, cache }) : undefined;
110
+ if (!cache || equalityKey === undefined) {
111
+ // compare the key values with the ones of every sibling entity
112
+ return records_1.Records.getChildren(nodeParent, entity.nodeDefUuid)(record).some(isDuplicateOf);
113
+ }
114
+ // look up the sibling entities with the same key values in the index (built once for all the siblings)
115
+ const index = _getSiblingEntitiesKeysIndex({
116
+ survey,
117
+ record,
118
+ nodeParent,
119
+ entityDefUuid: entity.nodeDefUuid,
120
+ keyDefs,
121
+ cache,
122
+ });
123
+ return (!!((_a = index.entitiesByEqualityKey.get(equalityKey)) === null || _a === void 0 ? void 0 : _a.some((siblingEntity) => !node_1.Nodes.areEqual(entity, siblingEntity))) ||
124
+ index.entitiesWithoutEqualityKey.some(isDuplicateOf));
68
125
  };
69
126
  const isNodeDefToBeValidated = (params) => {
70
127
  const { survey, nodeDef } = params;
@@ -1 +1 @@
1
- {"version":3,"file":"attributeKeyValidator.js","sourceRoot":"","sources":["../../../src/record/recordValidator/attributeKeyValidator.ts"],"names":[],"mappings":"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","sourcesContent":["import { ArenaRecord } from '../record'\nimport { Records } from '../records'\nimport { NodeDef, NodeDefs, NodeDefProps, NodeDefType } from '../../nodeDef'\nimport { ArenaRecordNode, Nodes, NodeValues } from '../../node'\nimport { ValidationResult, ValidationResultFactory, ValidationSeverity } from '../../validation'\nimport { Survey, Surveys } from '../../survey'\n\nimport { Dictionary } from '../../common'\nimport { Objects } from '../../utils'\n\n/**\n * Cache of entity key values (and key defs by entity def) used during a single validation run (the record doesn't change):\n * without it, validating the keys of S sibling entities would compute the key values of every sibling S times.\n */\nexport type EntityKeysCache = {\n keyDefsByEntityDefUuid: Map<string, NodeDef<NodeDefType, NodeDefProps>[]>\n keyValuesByEntityUuid: Map<string, Dictionary<any>>\n}\n\nexport const createEntityKeysCache = (): EntityKeysCache => ({\n keyDefsByEntityDefUuid: new Map(),\n keyValuesByEntityUuid: new Map(),\n})\n\nconst _getKeyDefs = (params: {\n survey: Survey\n entityDef: NodeDef<NodeDefType, NodeDefProps>\n cache?: EntityKeysCache\n}): NodeDef<NodeDefType, NodeDefProps>[] => {\n const { survey, entityDef, cache } = params\n let keyDefs = cache?.keyDefsByEntityDefUuid.get(entityDef.uuid)\n if (!keyDefs) {\n keyDefs = Surveys.getNodeDefKeys({ survey, nodeDef: entityDef })\n cache?.keyDefsByEntityDefUuid.set(entityDef.uuid, keyDefs)\n }\n return keyDefs\n}\n\nconst _getKeyValuesByDefUuid = (params: {\n survey: Survey\n record: ArenaRecord\n entity: ArenaRecordNode\n keyDefs: NodeDef<NodeDefType, NodeDefProps>[]\n cache?: EntityKeysCache\n}): Dictionary<any> => {\n const { survey, record, entity, keyDefs, cache } = params\n let keyValues = cache?.keyValuesByEntityUuid.get(entity.uuid)\n if (!keyValues) {\n keyValues = Records.getEntityKeyValuesByDefUuid(survey, entity, undefined, keyDefs)(record)\n cache?.keyValuesByEntityUuid.set(entity.uuid, keyValues)\n }\n return keyValues\n}\n\nconst _isEntityDuplicate = (params: {\n survey: Survey\n record: ArenaRecord\n entity: ArenaRecordNode\n entityKeysCache?: EntityKeysCache\n}): boolean => {\n const { survey, entity, record, entityKeysCache: cache } = params\n // 1. get sibling entities\n const nodeParent = Records.getParent(entity)(record)\n if (!nodeParent) return false\n\n const siblingEntities = Records.getChildren(\n nodeParent,\n entity.nodeDefUuid\n )(record).filter((node) => !Nodes.areEqual(entity, node))\n\n // 2. get key values\n const entityDef = Surveys.getNodeDefByUuid({ survey, uuid: entity.nodeDefUuid })\n const keyDefs = _getKeyDefs({ survey, entityDef, cache })\n\n if (Objects.isEmpty(siblingEntities) || Objects.isEmpty(keyDefs)) {\n return false\n }\n const keyValuesByDefUuid = _getKeyValuesByDefUuid({ survey, record, entity, keyDefs, cache })\n // entities with all key values empty are never duplicates (they are reported as missing values)\n return (\n !Object.values(keyValuesByDefUuid).every(Objects.isEmpty) &&\n siblingEntities.some((sibilingEntity) => {\n const siblingKeyValuesByDefUuid = _getKeyValuesByDefUuid({\n survey,\n record,\n entity: sibilingEntity,\n keyDefs,\n cache,\n })\n return keyDefs.every((keyDef) => {\n const keyValue = keyValuesByDefUuid[keyDef.uuid]\n const siblingKeyValue = siblingKeyValuesByDefUuid[keyDef.uuid]\n return NodeValues.isValueEqual({\n survey,\n nodeDef: keyDef,\n value: keyValue,\n valueSearch: siblingKeyValue,\n })\n })\n })\n )\n}\n\nconst isNodeDefToBeValidated = (params: { survey: Survey; nodeDef: NodeDef<NodeDefType, NodeDefProps> }): boolean => {\n const { survey, nodeDef } = params\n if (!NodeDefs.isKey(nodeDef)) return false\n\n const nodeDefParent = Surveys.getNodeDefParent({ survey, nodeDef })\n // root entity key attributes will be validated by another validator\n return !!nodeDefParent && !NodeDefs.isRoot(nodeDefParent)\n}\n\nconst validateAttributeKey =\n (params: {\n survey: Survey\n record: ArenaRecord\n nodeDef: NodeDef<NodeDefType, NodeDefProps>\n entityKeysCache?: EntityKeysCache\n }) =>\n (_propName: string, node: ArenaRecordNode): Promise<ValidationResult> => {\n const { survey, nodeDef, record, entityKeysCache } = params\n\n if (isNodeDefToBeValidated({ survey, nodeDef })) {\n const entity = Records.getParent(node)(record)\n if (entity && _isEntityDuplicate({ survey, record, entity, entityKeysCache })) {\n return Promise.resolve(\n ValidationResultFactory.createInstance({\n valid: false,\n severity: ValidationSeverity.error,\n key: 'record.entity.keyDuplicate',\n })\n )\n }\n }\n\n return Promise.resolve(ValidationResultFactory.createInstance())\n }\n\nexport const AttributeKeyValidator = {\n validateAttributeKey,\n}\n"]}
1
+ {"version":3,"file":"attributeKeyValidator.js","sourceRoot":"","sources":["../../../src/record/recordValidator/attributeKeyValidator.ts"],"names":[],"mappings":"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","sourcesContent":["import { ArenaRecord } from '../record'\nimport { Records } from '../records'\nimport { NodeDef, NodeDefs, NodeDefProps, NodeDefType } from '../../nodeDef'\nimport { ArenaRecordNode, Nodes, NodeValues } from '../../node'\nimport { getValueEqualityKey } from '../../node/nodeValues'\nimport { ValidationResult, ValidationResultFactory, ValidationSeverity } from '../../validation'\nimport { Survey, Surveys } from '../../survey'\n\nimport { Dictionary } from '../../common'\nimport { Objects } from '../../utils'\n\n/**\n * Sibling entities (same parent and entity def) indexed by the equality key of their key values:\n * entities with the same equality key are duplicates; entities whose equality key cannot be built (see getValueEqualityKey)\n * are compared with every sibling.\n */\ntype SiblingEntitiesKeysIndex = {\n entitiesByEqualityKey: Map<string, ArenaRecordNode[]>\n entitiesWithoutEqualityKey: ArenaRecordNode[]\n}\n\n/**\n * Cache of entity key values (and key defs by entity def) used during a single validation run (the record doesn't change):\n * without it, validating the keys of S sibling entities would compute the key values of every sibling S times\n * and compare them S times with the key values of every sibling.\n */\nexport type EntityKeysCache = {\n keyDefsByEntityDefUuid: Map<string, NodeDef<NodeDefType, NodeDefProps>[]>\n keyValuesByEntityUuid: Map<string, Dictionary<any>>\n equalityKeyByEntityUuid: Map<string, string | undefined>\n siblingEntitiesKeysIndexByParentAndDefUuid: Map<string, SiblingEntitiesKeysIndex>\n}\n\nexport const createEntityKeysCache = (): EntityKeysCache => ({\n keyDefsByEntityDefUuid: new Map(),\n keyValuesByEntityUuid: new Map(),\n equalityKeyByEntityUuid: new Map(),\n siblingEntitiesKeysIndexByParentAndDefUuid: new Map(),\n})\n\nconst _getKeyDefs = (params: {\n survey: Survey\n entityDef: NodeDef<NodeDefType, NodeDefProps>\n cache?: EntityKeysCache\n}): NodeDef<NodeDefType, NodeDefProps>[] => {\n const { survey, entityDef, cache } = params\n let keyDefs = cache?.keyDefsByEntityDefUuid.get(entityDef.uuid)\n if (!keyDefs) {\n keyDefs = Surveys.getNodeDefKeys({ survey, nodeDef: entityDef })\n cache?.keyDefsByEntityDefUuid.set(entityDef.uuid, keyDefs)\n }\n return keyDefs\n}\n\nconst _getKeyValuesByDefUuid = (params: {\n survey: Survey\n record: ArenaRecord\n entity: ArenaRecordNode\n keyDefs: NodeDef<NodeDefType, NodeDefProps>[]\n cache?: EntityKeysCache\n}): Dictionary<any> => {\n const { survey, record, entity, keyDefs, cache } = params\n let keyValues = cache?.keyValuesByEntityUuid.get(entity.uuid)\n if (!keyValues) {\n keyValues = Records.getEntityKeyValuesByDefUuid(survey, entity, undefined, keyDefs)(record)\n cache?.keyValuesByEntityUuid.set(entity.uuid, keyValues)\n }\n return keyValues\n}\n\nconst _getEqualityKey = (params: {\n survey: Survey\n entity: ArenaRecordNode\n keyDefs: NodeDef<NodeDefType, NodeDefProps>[]\n keyValuesByDefUuid: Dictionary<any>\n cache: EntityKeysCache\n}): string | undefined => {\n const { survey, entity, keyDefs, keyValuesByDefUuid, cache } = params\n if (cache.equalityKeyByEntityUuid.has(entity.uuid)) return cache.equalityKeyByEntityUuid.get(entity.uuid)\n\n const keyParts: string[] = []\n for (const keyDef of keyDefs) {\n const keyPart = getValueEqualityKey({ survey, nodeDef: keyDef, value: keyValuesByDefUuid[keyDef.uuid] })\n if (keyPart === undefined) {\n keyParts.length = 0\n break\n }\n keyParts.push(keyPart)\n }\n const equalityKey = keyParts.length > 0 ? JSON.stringify(keyParts) : undefined\n cache.equalityKeyByEntityUuid.set(entity.uuid, equalityKey)\n return equalityKey\n}\n\nconst _areKeyValuesEqual = (params: {\n survey: Survey\n keyDefs: NodeDef<NodeDefType, NodeDefProps>[]\n keyValuesByDefUuid: Dictionary<any>\n siblingKeyValuesByDefUuid: Dictionary<any>\n}): boolean => {\n const { survey, keyDefs, keyValuesByDefUuid, siblingKeyValuesByDefUuid } = params\n return keyDefs.every((keyDef) =>\n NodeValues.isValueEqual({\n survey,\n nodeDef: keyDef,\n value: keyValuesByDefUuid[keyDef.uuid],\n valueSearch: siblingKeyValuesByDefUuid[keyDef.uuid],\n })\n )\n}\n\nconst _getSiblingEntitiesKeysIndex = (params: {\n survey: Survey\n record: ArenaRecord\n nodeParent: ArenaRecordNode\n entityDefUuid: string\n keyDefs: NodeDef<NodeDefType, NodeDefProps>[]\n cache: EntityKeysCache\n}): SiblingEntitiesKeysIndex => {\n const { survey, record, nodeParent, entityDefUuid, keyDefs, cache } = params\n const indexKey = `${nodeParent.uuid}_${entityDefUuid}`\n let index = cache.siblingEntitiesKeysIndexByParentAndDefUuid.get(indexKey)\n if (!index) {\n index = { entitiesByEqualityKey: new Map(), entitiesWithoutEqualityKey: [] }\n for (const siblingEntity of Records.getChildren(nodeParent, entityDefUuid)(record)) {\n const keyValuesByDefUuid = _getKeyValuesByDefUuid({ survey, record, entity: siblingEntity, keyDefs, cache })\n const equalityKey = _getEqualityKey({ survey, entity: siblingEntity, keyDefs, keyValuesByDefUuid, cache })\n if (equalityKey === undefined) {\n index.entitiesWithoutEqualityKey.push(siblingEntity)\n } else {\n const entitiesWithSameKey = index.entitiesByEqualityKey.get(equalityKey)\n if (entitiesWithSameKey) {\n entitiesWithSameKey.push(siblingEntity)\n } else {\n index.entitiesByEqualityKey.set(equalityKey, [siblingEntity])\n }\n }\n }\n cache.siblingEntitiesKeysIndexByParentAndDefUuid.set(indexKey, index)\n }\n return index\n}\n\nconst _isEntityDuplicate = (params: {\n survey: Survey\n record: ArenaRecord\n entity: ArenaRecordNode\n entityKeysCache?: EntityKeysCache\n}): boolean => {\n const { survey, entity, record, entityKeysCache: cache } = params\n const nodeParent = Records.getParent(entity)(record)\n if (!nodeParent) return false\n\n const entityDef = Surveys.getNodeDefByUuid({ survey, uuid: entity.nodeDefUuid })\n const keyDefs = _getKeyDefs({ survey, entityDef, cache })\n if (Objects.isEmpty(keyDefs)) return false\n\n const keyValuesByDefUuid = _getKeyValuesByDefUuid({ survey, record, entity, keyDefs, cache })\n // entities with all key values empty are never duplicates (they are reported as missing values)\n if (Object.values(keyValuesByDefUuid).every(Objects.isEmpty)) return false\n\n const isDuplicateOf = (siblingEntity: ArenaRecordNode): boolean =>\n !Nodes.areEqual(entity, siblingEntity) &&\n _areKeyValuesEqual({\n survey,\n keyDefs,\n keyValuesByDefUuid,\n siblingKeyValuesByDefUuid: _getKeyValuesByDefUuid({ survey, record, entity: siblingEntity, keyDefs, cache }),\n })\n\n const equalityKey = cache ? _getEqualityKey({ survey, entity, keyDefs, keyValuesByDefUuid, cache }) : undefined\n if (!cache || equalityKey === undefined) {\n // compare the key values with the ones of every sibling entity\n return Records.getChildren(nodeParent, entity.nodeDefUuid)(record).some(isDuplicateOf)\n }\n // look up the sibling entities with the same key values in the index (built once for all the siblings)\n const index = _getSiblingEntitiesKeysIndex({\n survey,\n record,\n nodeParent,\n entityDefUuid: entity.nodeDefUuid,\n keyDefs,\n cache,\n })\n return (\n !!index.entitiesByEqualityKey.get(equalityKey)?.some((siblingEntity) => !Nodes.areEqual(entity, siblingEntity)) ||\n index.entitiesWithoutEqualityKey.some(isDuplicateOf)\n )\n}\n\nconst isNodeDefToBeValidated = (params: { survey: Survey; nodeDef: NodeDef<NodeDefType, NodeDefProps> }): boolean => {\n const { survey, nodeDef } = params\n if (!NodeDefs.isKey(nodeDef)) return false\n\n const nodeDefParent = Surveys.getNodeDefParent({ survey, nodeDef })\n // root entity key attributes will be validated by another validator\n return !!nodeDefParent && !NodeDefs.isRoot(nodeDefParent)\n}\n\nconst validateAttributeKey =\n (params: {\n survey: Survey\n record: ArenaRecord\n nodeDef: NodeDef<NodeDefType, NodeDefProps>\n entityKeysCache?: EntityKeysCache\n }) =>\n (_propName: string, node: ArenaRecordNode): Promise<ValidationResult> => {\n const { survey, nodeDef, record, entityKeysCache } = params\n\n if (isNodeDefToBeValidated({ survey, nodeDef })) {\n const entity = Records.getParent(node)(record)\n if (entity && _isEntityDuplicate({ survey, record, entity, entityKeysCache })) {\n return Promise.resolve(\n ValidationResultFactory.createInstance({\n valid: false,\n severity: ValidationSeverity.error,\n key: 'record.entity.keyDuplicate',\n })\n )\n }\n }\n\n return Promise.resolve(ValidationResultFactory.createInstance())\n }\n\nexport const AttributeKeyValidator = {\n validateAttributeKey,\n}\n"]}
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "name": "OpenForis",
5
5
  "email": "openforis.arena@gmail.com"
6
6
  },
7
- "version": "2.18.16",
7
+ "version": "2.18.18",
8
8
  "description": "Arena core module",
9
9
  "repository": {
10
10
  "type": "git",