mirror of
https://github.com/PeWu/topola-viewer.git
synced 2026-02-18 02:55:48 +00:00
Initialize search from JsonGedcomData instead of raw gedcom
This commit is contained in:
parent
36dabfc4a1
commit
c9d5dac0f2
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user