Initialize search from JsonGedcomData instead of raw gedcom

This commit is contained in:
Przemek Wiech 2020-04-02 18:19:51 +02:00
parent 36dabfc4a1
commit c9d5dac0f2
6 changed files with 77 additions and 82 deletions

View File

@ -566,7 +566,7 @@ export class App extends React.Component<RouteComponentProps, {}> {
render={(props: RouteComponentProps) => ( render={(props: RouteComponentProps) => (
<TopBar <TopBar
{...props} {...props}
gedcom={this.state.data && this.state.data.gedcom} data={this.state.data && this.state.data.chartData}
allowAllRelativesChart={ allowAllRelativesChart={
this.state.source !== DataSourceEnum.WIKITREE this.state.source !== DataSourceEnum.WIKITREE
} }

View File

@ -1,4 +1,4 @@
import {Date as TopolaDate, DateRange, getDate} from 'topola'; import {Date as TopolaDate, DateRange, getDate, DateOrRange} from 'topola';
import {InjectedIntl} from 'react-intl'; import {InjectedIntl} from 'react-intl';
const DATE_QUALIFIERS = new Map([ const DATE_QUALIFIERS = new Map([
@ -76,9 +76,11 @@ function formatDateRage(dateRange: DateRange, intl: InjectedIntl) {
return ''; return '';
} }
/** Formats a date given in GEDCOM format. */ /** Formats a DateOrRange object. */
export function translateDate(gedcomDate: string, intl: InjectedIntl) { export function formatDateOrRange(
const dateOrRange = getDate(gedcomDate); dateOrRange: DateOrRange | undefined,
intl: InjectedIntl,
): string {
if (!dateOrRange) { if (!dateOrRange) {
return ''; return '';
} }
@ -90,3 +92,8 @@ export function translateDate(gedcomDate: string, intl: InjectedIntl) {
} }
return ''; return '';
} }
/** Formats a date given in GEDCOM format. */
export function translateDate(gedcomDate: string, intl: InjectedIntl): string {
return formatDateOrRange(getDate(gedcomDate), intl);
}

View File

@ -32,6 +32,22 @@ export function pointerToId(pointer: string): string {
return pointer.substring(1, pointer.length - 1); return pointer.substring(1, pointer.length - 1);
} }
export function idToIndiMap(data: JsonGedcomData): Map<string, JsonIndi> {
const map = new Map<string, JsonIndi>();
data.indis.forEach((indi) => {
map.set(indi.id, indi);
});
return map;
}
export function idToFamMap(data: JsonGedcomData): Map<string, JsonFam> {
const map = new Map<string, JsonFam>();
data.fams.forEach((fam) => {
map.set(fam.id, fam);
});
return map;
}
function prepareGedcom(entries: GedcomEntry[]): GedcomData { function prepareGedcom(entries: GedcomEntry[]): GedcomData {
const head = entries.find((entry) => entry.tag === 'HEAD')!; const head = entries.find((entry) => entry.tag === 'HEAD')!;
const indis: {[key: string]: GedcomEntry} = {}; const indis: {[key: string]: GedcomEntry} = {};
@ -88,14 +104,11 @@ function compareDates(
/** Birth date comparator for individuals. */ /** Birth date comparator for individuals. */
function birthDatesComparator(gedcom: JsonGedcomData) { function birthDatesComparator(gedcom: JsonGedcomData) {
const idToIndiMap = new Map<string, JsonIndi>(); const indiMap = idToIndiMap(gedcom);
gedcom.indis.forEach((indi) => {
idToIndiMap[indi.id] = indi;
});
return (indiId1: string, indiId2: string) => { return (indiId1: string, indiId2: string) => {
const indi1: JsonIndi = idToIndiMap[indiId1]; const indi1: JsonIndi | undefined = indiMap.get(indiId1);
const indi2: JsonIndi = idToIndiMap[indiId2]; const indi2: JsonIndi | undefined = indiMap.get(indiId2);
return ( return (
compareDates(indi1 && indi1.birth, indi2 && indi2.birth) || compareDates(indi1 && indi1.birth, indi2 && indi2.birth) ||
strcmp(indiId1, indiId2) strcmp(indiId1, indiId2)
@ -105,14 +118,11 @@ function birthDatesComparator(gedcom: JsonGedcomData) {
/** Marriage date comparator for families. */ /** Marriage date comparator for families. */
function marriageDatesComparator(gedcom: JsonGedcomData) { function marriageDatesComparator(gedcom: JsonGedcomData) {
const idToFamMap = new Map<string, JsonFam>(); const famMap = idToFamMap(gedcom);
gedcom.fams.forEach((fam) => {
idToFamMap[fam.id] = fam;
});
return (famId1: string, famId2: string) => { return (famId1: string, famId2: string) => {
const fam1: JsonFam = idToFamMap[famId1]; const fam1: JsonFam | undefined = famMap.get(famId1);
const fam2: JsonFam = idToFamMap[famId2]; const fam2: JsonFam | undefined = famMap.get(famId2);
return ( return (
compareDates(fam1 && fam1.marriage, fam2 && fam2.marriage) || compareDates(fam1 && fam1.marriage, fam2 && fam2.marriage) ||
strcmp(famId1, famId2) strcmp(famId1, famId2)

View File

@ -1,13 +1,13 @@
import naturalSort from 'javascript-natural-sort'; import naturalSort from 'javascript-natural-sort';
import lunr from 'lunr'; import lunr from 'lunr';
import {GedcomData, pointerToId} from './gedcom_util'; import {idToIndiMap, idToFamMap} from './gedcom_util';
import {GedcomEntry} from 'parse-gedcom'; import {JsonIndi, JsonFam, JsonGedcomData} from 'topola';
const MAX_RESULTS = 8; const MAX_RESULTS = 8;
export interface SearchResult { export interface SearchResult {
id: string; id: string;
indi: GedcomEntry; indi: JsonIndi;
} }
export interface SearchIndex { export interface SearchIndex {
@ -32,36 +32,28 @@ function compare(a: lunr.Index.Result, b: lunr.Index.Result) {
} }
/** Returns all last names of all husbands as a space-separated string. */ /** Returns all last names of all husbands as a space-separated string. */
function getHusbandLastName(indi: GedcomEntry, gedcom: GedcomData): string { function getHusbandLastName(
return indi.tree indi: JsonIndi,
.filter((entry) => entry.tag === 'FAMS') indiMap: Map<String, JsonIndi>,
.map((entry) => gedcom.fams[pointerToId(entry.data)]) famMap: Map<string, JsonFam>,
.filter((entry) => !!entry) ): string {
.map((entry) => { return (indi.fams || [])
const husband = entry.tree.find((entry) => entry.tag === 'HUSB'); .map((famId) => famMap.get(famId))
const husbandId = husband && pointerToId(husband.data); .map((fam) => fam && fam.husb)
return ( .map((husbId) => husbId && indiMap.get(husbId))
husbandId && .map((husband) => husband && husband.lastName)
husbandId !== pointerToId(indi.pointer) &&
gedcom.indis[husbandId]
);
})
.filter((entry) => !!entry)
.flatMap((husband) =>
(husband as GedcomEntry).tree
.filter((entry) => entry.tag === 'NAME')
.map((entry) => {
const names = entry.data.split('/');
return names.length >= 2 ? names[1] : '';
}),
)
.join(' '); .join(' ');
} }
class LunrSearchIndex implements SearchIndex { class LunrSearchIndex implements SearchIndex {
private index: lunr.Index | undefined; private index: lunr.Index | undefined;
private indiMap: Map<string, JsonIndi>;
private famMap: Map<string, JsonFam>;
constructor(private gedcom: GedcomData) {} constructor(data: JsonGedcomData) {
this.indiMap = idToIndiMap(data);
this.famMap = idToFamMap(data);
}
initialize() { initialize() {
const self = this; const self = this;
@ -73,25 +65,25 @@ class LunrSearchIndex implements SearchIndex {
this.field('spouseLastName', {boost: 2}); this.field('spouseLastName', {boost: 2});
this.field('normalizedSpouseLastName', {boost: 2}); this.field('normalizedSpouseLastName', {boost: 2});
for (let id in self.gedcom.indis) { self.indiMap.forEach((indi) => {
const indi = self.gedcom.indis[id]; const name = [indi.firstName, indi.lastName].join(' ');
const name = indi.tree const spouseLastName = getHusbandLastName(
.filter((entry) => entry.tag === 'NAME') indi,
.map((entry) => entry.data) self.indiMap,
.join(' '); self.famMap,
const spouseLastName = getHusbandLastName(indi, self.gedcom); );
this.add({ this.add({
id, id: indi.id,
name, name,
normalizedName: normalize(name), normalizedName: normalize(name),
spouseLastName, spouseLastName,
normalizedSpouseLastName: normalize(spouseLastName), normalizedSpouseLastName: normalize(spouseLastName),
}); });
} });
}); });
} }
public search(input: string) { public search(input: string): SearchResult[] {
const query = input const query = input
.split(' ') .split(' ')
.filter((s) => !!s) .filter((s) => !!s)
@ -101,13 +93,13 @@ class LunrSearchIndex implements SearchIndex {
return results return results
.sort(compare) .sort(compare)
.slice(0, MAX_RESULTS) .slice(0, MAX_RESULTS)
.map((result) => ({id: result.ref, indi: this.gedcom.indis[result.ref]})); .map((result) => ({id: result.ref, indi: this.indiMap.get(result.ref)!}));
} }
} }
/** Builds a search index from data. */ /** Builds a search index from data. */
export function buildSearchIndex(gedcom: GedcomData): SearchIndex { export function buildSearchIndex(data: JsonGedcomData): SearchIndex {
const index = new LunrSearchIndex(gedcom); const index = new LunrSearchIndex(data);
index.initialize(); index.initialize();
return index; return index;
} }

View File

@ -1,17 +1,11 @@
import * as React from 'react'; import * as React from 'react';
import {GedcomEntry} from 'parse-gedcom';
import {InjectedIntl} from 'react-intl'; import {InjectedIntl} from 'react-intl';
import {SearchResult} from './search_index'; import {SearchResult} from './search_index';
import {translateDate} from './date_util'; import {formatDateOrRange} from './date_util';
import {JsonIndi} from 'topola';
function getNameLine(result: SearchResult) { function getNameLine(result: SearchResult) {
const nameTag = result.indi.tree.find((entry) => entry.tag === 'NAME'); const name = [result.indi.firstName, result.indi.lastName].join(' ').trim();
const name =
nameTag &&
nameTag.data
.split('/')
.filter((s) => !!s)
.join(' ');
if (result.id.length > 8) { if (result.id.length > 8) {
return name; return name;
} }
@ -22,16 +16,9 @@ function getNameLine(result: SearchResult) {
); );
} }
function getDate(indi: GedcomEntry, tag: string, intl: InjectedIntl) { function getDescriptionLine(indi: JsonIndi, intl: InjectedIntl) {
const eventEntry = indi.tree.find((entry) => entry.tag === tag); const birthDate = formatDateOrRange(indi.birth, intl);
const dateEntry = const deathDate = formatDateOrRange(indi.death, intl);
eventEntry && eventEntry.tree.find((entry) => entry.tag === 'DATE');
return (dateEntry && translateDate(dateEntry.data, intl)) || '';
}
function getDescriptionLine(indi: GedcomEntry, intl: InjectedIntl) {
const birthDate = getDate(indi, 'BIRT', intl);
const deathDate = getDate(indi, 'DEAT', intl);
if (!deathDate) { if (!deathDate) {
return birthDate; return birthDate;
} }

View File

@ -7,8 +7,7 @@ import {analyticsEvent} from './analytics';
import {buildSearchIndex, SearchIndex} from './search_index'; import {buildSearchIndex, SearchIndex} from './search_index';
import {displaySearchResult} from './search_util'; import {displaySearchResult} from './search_util';
import {FormattedMessage, intlShape} from 'react-intl'; import {FormattedMessage, intlShape} from 'react-intl';
import {GedcomData} from './gedcom_util'; import {IndiInfo, JsonGedcomData} from 'topola';
import {IndiInfo} from 'topola';
import {Link} from 'react-router-dom'; import {Link} from 'react-router-dom';
import {RouteComponentProps} from 'react-router-dom'; import {RouteComponentProps} from 'react-router-dom';
import { import {
@ -58,7 +57,7 @@ interface Props {
/** True if the application is currently showing a chart. */ /** True if the application is currently showing a chart. */
showingChart: boolean; showingChart: boolean;
/** Data used for the search index. */ /** Data used for the search index. */
gedcom?: GedcomData; data?: JsonGedcomData;
standalone: boolean; standalone: boolean;
/** Whether to show the "All relatives" chart type in the menu. */ /** Whether to show the "All relatives" chart type in the menu. */
allowAllRelativesChart: boolean; allowAllRelativesChart: boolean;
@ -213,8 +212,8 @@ export class TopBar extends React.Component<
} }
private initializeSearchIndex() { private initializeSearchIndex() {
if (this.props.gedcom) { if (this.props.data) {
this.searchIndex = buildSearchIndex(this.props.gedcom); this.searchIndex = buildSearchIndex(this.props.data);
} }
} }
@ -265,7 +264,7 @@ export class TopBar extends React.Component<
componentDidUpdate(prevProps: Props) { componentDidUpdate(prevProps: Props) {
this.checkWikiTreeLoginState(); this.checkWikiTreeLoginState();
if (prevProps.gedcom !== this.props.gedcom) { if (prevProps.data !== this.props.data) {
this.initializeSearchIndex(); this.initializeSearchIndex();
} }
} }