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) => (
<TopBar
{...props}
gedcom={this.state.data && this.state.data.gedcom}
data={this.state.data && this.state.data.chartData}
allowAllRelativesChart={
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';
const DATE_QUALIFIERS = new Map([
@ -76,9 +76,11 @@ function formatDateRage(dateRange: DateRange, intl: InjectedIntl) {
return '';
}
/** Formats a date given in GEDCOM format. */
export function translateDate(gedcomDate: string, intl: InjectedIntl) {
const dateOrRange = getDate(gedcomDate);
/** Formats a DateOrRange object. */
export function formatDateOrRange(
dateOrRange: DateOrRange | undefined,
intl: InjectedIntl,
): string {
if (!dateOrRange) {
return '';
}
@ -90,3 +92,8 @@ export function translateDate(gedcomDate: string, intl: InjectedIntl) {
}
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);
}
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 {
const head = entries.find((entry) => entry.tag === 'HEAD')!;
const indis: {[key: string]: GedcomEntry} = {};
@ -88,14 +104,11 @@ function compareDates(
/** Birth date comparator for individuals. */
function birthDatesComparator(gedcom: JsonGedcomData) {
const idToIndiMap = new Map<string, JsonIndi>();
gedcom.indis.forEach((indi) => {
idToIndiMap[indi.id] = indi;
});
const indiMap = idToIndiMap(gedcom);
return (indiId1: string, indiId2: string) => {
const indi1: JsonIndi = idToIndiMap[indiId1];
const indi2: JsonIndi = idToIndiMap[indiId2];
const indi1: JsonIndi | undefined = indiMap.get(indiId1);
const indi2: JsonIndi | undefined = indiMap.get(indiId2);
return (
compareDates(indi1 && indi1.birth, indi2 && indi2.birth) ||
strcmp(indiId1, indiId2)
@ -105,14 +118,11 @@ function birthDatesComparator(gedcom: JsonGedcomData) {
/** Marriage date comparator for families. */
function marriageDatesComparator(gedcom: JsonGedcomData) {
const idToFamMap = new Map<string, JsonFam>();
gedcom.fams.forEach((fam) => {
idToFamMap[fam.id] = fam;
});
const famMap = idToFamMap(gedcom);
return (famId1: string, famId2: string) => {
const fam1: JsonFam = idToFamMap[famId1];
const fam2: JsonFam = idToFamMap[famId2];
const fam1: JsonFam | undefined = famMap.get(famId1);
const fam2: JsonFam | undefined = famMap.get(famId2);
return (
compareDates(fam1 && fam1.marriage, fam2 && fam2.marriage) ||
strcmp(famId1, famId2)

View File

@ -1,13 +1,13 @@
import naturalSort from 'javascript-natural-sort';
import lunr from 'lunr';
import {GedcomData, pointerToId} from './gedcom_util';
import {GedcomEntry} from 'parse-gedcom';
import {idToIndiMap, idToFamMap} from './gedcom_util';
import {JsonIndi, JsonFam, JsonGedcomData} from 'topola';
const MAX_RESULTS = 8;
export interface SearchResult {
id: string;
indi: GedcomEntry;
indi: JsonIndi;
}
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. */
function getHusbandLastName(indi: GedcomEntry, gedcom: GedcomData): string {
return indi.tree
.filter((entry) => entry.tag === 'FAMS')
.map((entry) => gedcom.fams[pointerToId(entry.data)])
.filter((entry) => !!entry)
.map((entry) => {
const husband = entry.tree.find((entry) => entry.tag === 'HUSB');
const husbandId = husband && pointerToId(husband.data);
return (
husbandId &&
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] : '';
}),
)
function getHusbandLastName(
indi: JsonIndi,
indiMap: Map<String, JsonIndi>,
famMap: Map<string, JsonFam>,
): string {
return (indi.fams || [])
.map((famId) => famMap.get(famId))
.map((fam) => fam && fam.husb)
.map((husbId) => husbId && indiMap.get(husbId))
.map((husband) => husband && husband.lastName)
.join(' ');
}
class LunrSearchIndex implements SearchIndex {
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() {
const self = this;
@ -73,25 +65,25 @@ class LunrSearchIndex implements SearchIndex {
this.field('spouseLastName', {boost: 2});
this.field('normalizedSpouseLastName', {boost: 2});
for (let id in self.gedcom.indis) {
const indi = self.gedcom.indis[id];
const name = indi.tree
.filter((entry) => entry.tag === 'NAME')
.map((entry) => entry.data)
.join(' ');
const spouseLastName = getHusbandLastName(indi, self.gedcom);
self.indiMap.forEach((indi) => {
const name = [indi.firstName, indi.lastName].join(' ');
const spouseLastName = getHusbandLastName(
indi,
self.indiMap,
self.famMap,
);
this.add({
id,
id: indi.id,
name,
normalizedName: normalize(name),
spouseLastName,
normalizedSpouseLastName: normalize(spouseLastName),
});
}
});
});
}
public search(input: string) {
public search(input: string): SearchResult[] {
const query = input
.split(' ')
.filter((s) => !!s)
@ -101,13 +93,13 @@ class LunrSearchIndex implements SearchIndex {
return results
.sort(compare)
.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. */
export function buildSearchIndex(gedcom: GedcomData): SearchIndex {
const index = new LunrSearchIndex(gedcom);
export function buildSearchIndex(data: JsonGedcomData): SearchIndex {
const index = new LunrSearchIndex(data);
index.initialize();
return index;
}

View File

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

View File

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