Skip to content

Commit f69af0f

Browse files
committed
chore: only use precursor for peak difference creations
1 parent ddf61e4 commit f69af0f

File tree

10 files changed

+126
-86
lines changed

10 files changed

+126
-86
lines changed

pkg/database/postgres.go

Lines changed: 50 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"errors"
66
"fmt"
77
"log"
8-
"math"
98
"regexp"
109
"slices"
1110
"strconv"
@@ -112,10 +111,10 @@ func (db *PostgresSQLDB) GetIndexes() []Index {
112111
indexes = append(indexes, Index{IndexName: "browse_options_atomcount_index", TableName: "browse_options", Columns: []string{"atomcount"}})
113112

114113
indexes = append(indexes, Index{IndexName: "peak_differences_spectrum_id_index", TableName: "peak_differences", Columns: []string{"spectrum_id"}})
114+
indexes = append(indexes, Index{IndexName: "peak_differences_peak_id_index", TableName: "peak_differences", Columns: []string{"peak_id"}})
115+
indexes = append(indexes, Index{IndexName: "peak_differences_precursor_mass_id_index", TableName: "peak_differences", Columns: []string{"precursor_mass"}})
115116
indexes = append(indexes, Index{IndexName: "peak_differences_difference_index", TableName: "peak_differences", Columns: []string{"difference"}})
116-
indexes = append(indexes, Index{IndexName: "peak_differences_peak1_id_index", TableName: "peak_differences", Columns: []string{"peak1_id"}})
117-
indexes = append(indexes, Index{IndexName: "peak_differences_peak2_id_index", TableName: "peak_differences", Columns: []string{"peak2_id"}})
118-
indexes = append(indexes, Index{IndexName: "peak_differences_min_rel_intensity_index", TableName: "peak_differences", Columns: []string{"min_rel_intensity"}})
117+
indexes = append(indexes, Index{IndexName: "peak_differences_rel_intensity_index", TableName: "peak_differences", Columns: []string{"rel_intensity"}})
119118

120119
indexes = append(indexes, Index{IndexName: "records_massbank_id_index", TableName: "records", Columns: []string{"massbank_id"}})
121120
indexes = append(indexes, Index{IndexName: "records_accession_index", TableName: "records", Columns: []string{"accession"}})
@@ -1147,10 +1146,10 @@ func (p *PostgresSQLDB) NeutralLossSearch(neutralLoss *[]float64, tolerance *flo
11471146
for i := 0; i < neutralLossesCount; i++ {
11481147
parameters = append(parameters, strconv.FormatFloat((*neutralLoss)[i]-*tolerance, 'f', -1, 64))
11491148
parameters = append(parameters, strconv.FormatFloat((*neutralLoss)[i]+*tolerance, 'f', -1, 64))
1150-
with = with + "t" + strconv.Itoa(i+1) + " AS (SELECT spectrum_id, array_agg(ARRAY[peak1_id, peak2_id]) AS agg FROM peak_differences WHERE difference BETWEEN $" + strconv.Itoa(len(parameters)-1) + " AND $" + strconv.Itoa(len(parameters))
1149+
with = with + "t" + strconv.Itoa(i+1) + " AS (SELECT spectrum_id, array_agg(ARRAY[peak_id, precursor_mass]) AS agg FROM peak_differences WHERE difference BETWEEN $" + strconv.Itoa(len(parameters)-1) + " AND $" + strconv.Itoa(len(parameters))
11511150
if minRelIntensity != nil {
11521151
parameters = append(parameters, strconv.FormatInt(*minRelIntensity, 10))
1153-
with = with + " AND min_rel_intensity >= $" + strconv.Itoa(len(parameters))
1152+
with = with + " AND rel_intensity >= $" + strconv.Itoa(len(parameters))
11541153
}
11551154
with = with + " GROUP BY spectrum_id)"
11561155
if i > 0 {
@@ -1167,6 +1166,7 @@ func (p *PostgresSQLDB) NeutralLossSearch(neutralLoss *[]float64, tolerance *flo
11671166
selectAggregations = selectAggregations + ")::json"
11681167

11691168
query = with + " SELECT " + selectAggregations + ", browse_options.accession, browse_options.atomcount " + from + " JOIN spectrum ON spectrum.id = t1.spectrum_id JOIN browse_options ON browse_options.massbank_id = spectrum.massbank_id;"
1169+
11701170
stmt, err := p.database.Prepare(query)
11711171
if err != nil {
11721172
return nil, nil, nil, err
@@ -1880,40 +1880,43 @@ func (p *PostgresSQLDB) AddRecord(record *massbank.MassBank2, metaDataId string,
18801880
return err
18811881
}
18821882

1883-
if (msType == "MS2" || msType == "MS3" || msType == "MS4") && *record.Peak.NumPeak > 1 {
1884-
var sb strings.Builder
1885-
sb.WriteString("INSERT INTO peak_differences (spectrum_id, difference, peak1_id, peak2_id, min_rel_intensity) VALUES")
1886-
var diff float64
1887-
var toInsert = false
1888-
for i := 0; i < len(insertedPeaks.Id); i++ {
1889-
for j := i + 1; j < len(insertedPeaks.Id); j++ {
1890-
if insertedPeaks.Mz[i] >= insertedPeaks.Mz[j] {
1891-
diff = insertedPeaks.Mz[i] - insertedPeaks.Mz[j]
1892-
} else {
1893-
diff = insertedPeaks.Mz[j] - insertedPeaks.Mz[i]
1894-
}
1895-
if diff > 11.5 {
1896-
sb.WriteString(fmt.Sprintf(" (%d, %f, %d, %d, %f)", spectrumId, diff, insertedPeaks.Id[i], insertedPeaks.Id[j], math.Min(float64(insertedPeaks.Rel[i]), float64(insertedPeaks.Rel[j]))))
1897-
if j < len(insertedPeaks.Id)-1 {
1883+
if (msType == "MS2" || msType == "MS3" || msType == "MS4") && *record.Peak.NumPeak > 1 && record.MassSpectrometry.FocusedIon != nil {
1884+
var precursorStr = ""
1885+
for _, subProp := range *record.MassSpectrometry.FocusedIon {
1886+
if subProp.Subtag == "PRECURSOR_M/Z" {
1887+
precursorStr = subProp.Value
1888+
break
1889+
}
1890+
}
1891+
if precursorStr != "" {
1892+
precursorMass, err := strconv.ParseFloat(precursorStr, 64)
1893+
if err == nil {
1894+
var sb strings.Builder
1895+
sb.WriteString("INSERT INTO peak_differences (spectrum_id, peak_id, precursor_mass, difference, rel_intensity) VALUES")
1896+
var diff float64
1897+
var toInsert = false
1898+
for i := 0; i < len(insertedPeaks.Id); i++ {
1899+
if insertedPeaks.Mz[i] >= precursorMass {
1900+
diff = insertedPeaks.Mz[i] - precursorMass
1901+
} else {
1902+
diff = precursorMass - insertedPeaks.Mz[i]
1903+
}
1904+
sb.WriteString(fmt.Sprintf(" (%d, %d, %f, %f, %f)", spectrumId, insertedPeaks.Id[i], precursorMass, diff, float64(insertedPeaks.Rel[i])))
1905+
if i < len(insertedPeaks.Id)-1 {
18981906
sb.WriteString(",")
18991907
}
19001908
toInsert = true
1901-
} else {
1902-
toInsert = false
19031909
}
1904-
}
1905-
if toInsert && i < len(insertedPeaks.Id)-2 {
1906-
sb.WriteString(",")
1907-
}
1908-
}
1909-
if toInsert {
1910-
sb.WriteString(";")
1911-
_, err = tx.Exec(sb.String())
1912-
if err != nil {
1913-
if err2 := tx.Rollback(); err2 != nil {
1914-
return errors.New("Could not rollback after error: " + err2.Error() + "\n:" + err.Error())
1910+
if toInsert {
1911+
sb.WriteString(";")
1912+
_, err = tx.Exec(sb.String())
1913+
if err != nil {
1914+
if err2 := tx.Rollback(); err2 != nil {
1915+
return errors.New("Could not rollback after error: " + err2.Error() + "\n:" + err.Error())
1916+
}
1917+
return err
1918+
}
19151919
}
1916-
return err
19171920
}
19181921
}
19191922
}
@@ -1936,6 +1939,7 @@ func (p *PostgresSQLDB) AddRecord(record *massbank.MassBank2, metaDataId string,
19361939
if err2 := tx.Rollback(); err2 != nil {
19371940
return errors.New("Could not rollback after error: " + err2.Error() + "\n:" + err.Error())
19381941
}
1942+
return err
19391943
}
19401944

19411945
err = tx.Commit()
@@ -1955,13 +1959,19 @@ func (p *PostgresSQLDB) AddRecords(records []*massbank.MassBank2, metaDataId str
19551959
return errors.New("records and mb3RecordJsons must have the same length")
19561960
}
19571961

1962+
start := time.Now()
1963+
19581964
for i, record := range records {
19591965
err := p.AddRecord(record, metaDataId, mb3RecordJsons[i])
19601966
if err != nil {
19611967
return err
19621968
}
19631969
}
19641970

1971+
elapsed := time.Since(start)
1972+
fmt.Printf("\n -> AddRecord function took %s", elapsed)
1973+
fmt.Println()
1974+
19651975
return nil
19661976
}
19671977

@@ -2163,11 +2173,11 @@ func (p *PostgresSQLDB) Init() error {
21632173
);
21642174
21652175
CREATE TABLE peak_differences (
2166-
spectrum_id INT NOT NULL REFERENCES spectrum(id) ON UPDATE CASCADE ON DELETE CASCADE,
2167-
difference FLOAT NOT NULL,
2168-
peak1_id INT NOT NULL REFERENCES peak(id) ON UPDATE CASCADE ON DELETE CASCADE,
2169-
peak2_id INT NOT NULL REFERENCES peak(id) ON UPDATE CASCADE ON DELETE CASCADE,
2170-
min_rel_intensity FLOAT NOT NULL
2176+
spectrum_id INT NOT NULL REFERENCES spectrum(id) ON UPDATE CASCADE ON DELETE CASCADE,
2177+
peak_id INT NOT NULL REFERENCES peak(id) ON UPDATE CASCADE ON DELETE CASCADE,
2178+
precursor_mass FLOAT NOT NULL,
2179+
difference FLOAT NOT NULL,
2180+
rel_intensity FLOAT NOT NULL
21712181
);
21722182
21732183
CREATE TABLE IF NOT EXISTS records (

pkg/mb3dbtool/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ func main() {
9090

9191
err = db.AddRecords(mbfiles, metaId, mb3RecordStrings)
9292
if err != nil {
93+
println("Could not add records: " + err.Error())
9394
panic(err)
9495
}
9596

web-frontend/src/elements/basic/Chart.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -125,10 +125,8 @@ function Chart({
125125

126126
const filteredNeutralLossData = useMemo(
127127
() =>
128-
neutralLossData.filter(
129-
(nl) =>
130-
filteredPeakData.some((p) => p.id === nl.peak1_id) &&
131-
filteredPeakData.some((p) => p.id === nl.peak2_id),
128+
neutralLossData.filter((nl) =>
129+
filteredPeakData.some((p) => p.id === nl.peak_id),
132130
),
133131
[filteredPeakData, neutralLossData],
134132
);
@@ -401,7 +399,6 @@ function Chart({
401399
xScale={xScale}
402400
yScale={yScale}
403401
showLabel={isShowLabel}
404-
strokeColour="red"
405402
disableOnHover={disableOnHover}
406403
/>
407404
))

web-frontend/src/elements/basic/ChartElement.tsx

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ function ChartElement({
2020
xScale,
2121
yScale,
2222
showLabel = false,
23-
strokeColour = 'red',
23+
strokeColour,
2424
disableOnHover = false,
2525
}: InputProps) {
2626
const highlight = useHighlight([pd.id]);
@@ -74,27 +74,44 @@ function ChartElement({
7474
style={
7575
highlight.isActive
7676
? { opacity: 1, stroke: 'black', strokeWidth: 2 }
77-
: { stroke: strokeColour }
77+
: {
78+
stroke: strokeColour
79+
? strokeColour
80+
: pd.id === 'precursor'
81+
? 'blue'
82+
: 'red',
83+
}
7884
}
7985
>
8086
<line
8187
x1={xScaled}
8288
x2={xScaled}
8389
y1={yScale.range()[0]}
84-
y2={yScale(pd.rel || 0)}
90+
y2={yScale(pd.rel ?? 0)}
8591
/>
8692
{highlight.isActive && (
87-
<circle cx={xScaled} cy={yScale(pd.rel || 0)} r={3} />
93+
<circle cx={xScaled} cy={yScale(pd.rel ?? 0)} r={3} />
8894
)}
89-
{((!disableShowLabel && showLabel) || highlight.isActive) && (
95+
{pd.id === 'precursor' ? (
9096
<text
9197
className="hover-label"
92-
transform={`translate(${xScale(pd.mz)} ${
93-
pd.rel < 0 ? yScale(pd.rel || 0) + 20 : yScale(pd.rel || 0) - 10
94-
}) rotate(-30)`}
98+
transform={`translate(${xScale(pd.mz) - 50} ${
99+
pd.rel < 0 ? yScale(pd.rel ?? 0) + 20 : yScale(pd.rel ?? 0) - 10
100+
})`}
95101
>
96-
{pd.mz.toFixed(4)}
102+
{'precursor: ' + pd.mz.toFixed(4)}
97103
</text>
104+
) : (
105+
((!disableShowLabel && showLabel) || highlight.isActive) && (
106+
<text
107+
className="hover-label"
108+
transform={`translate(${xScale(pd.mz)} ${
109+
pd.rel < 0 ? yScale(pd.rel ?? 0) + 20 : yScale(pd.rel ?? 0) - 10
110+
}) rotate(-30)`}
111+
>
112+
{pd.mz.toFixed(4)}
113+
</text>
114+
)
98115
)}
99116
</g>
100117
),
@@ -104,6 +121,7 @@ function ChartElement({
104121
handleOnMouseEnter,
105122
handleOnMouseLeave,
106123
highlight.isActive,
124+
pd.id,
107125
pd.mz,
108126
pd.rel,
109127
showLabel,

web-frontend/src/elements/common/Resizable.tsx

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { Content } from 'antd/es/layout/layout';
88
import { Splitter, Tabs, TabsProps } from 'antd';
99
import NeutralLossTable from '../record/NeutralLossTable';
1010
import NeutralLoss from '../../types/peak/NeutralLoss';
11+
import getRecordPeakListWithPrecursor from '../../utils/getRecordPeakListWithPrecursor';
1112

1213
const sidebarWidth = 7;
1314
const tabsHeight = 55;
@@ -44,7 +45,7 @@ function Resizable({
4445
}, [minPeakTableWith, width]);
4546

4647
const [filteredPeakData, setFilteredPeakData] = useState<Peak[]>(
47-
record.peak.peak.values,
48+
getRecordPeakListWithPrecursor(record),
4849
);
4950

5051
const [filteredPeakData2, setFilteredPeakData2] = useState<
@@ -82,7 +83,7 @@ function Resizable({
8283
const chart = useMemo(
8384
() => (
8485
<Chart
85-
peakData={record.peak.peak.values}
86+
peakData={getRecordPeakListWithPrecursor(record)}
8687
peakData2={record2 ? record2.peak.peak.values : undefined}
8788
neutralLossData={record.peak.neutral_loss}
8889
onZoom={handleOnZoom}
@@ -91,15 +92,7 @@ function Resizable({
9192
disableExport={disableExport}
9293
/>
9394
),
94-
[
95-
chartWidth,
96-
disableExport,
97-
handleOnZoom,
98-
height,
99-
record.peak.neutral_loss,
100-
record.peak.peak.values,
101-
record2,
102-
],
95+
[chartWidth, disableExport, handleOnZoom, height, record, record2],
10396
);
10497

10598
const peakTable = useMemo(() => {

web-frontend/src/elements/record/NeutralLossTable.tsx

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ function NeutralLossTable({ neutralLosses, peaks, width, height }: InputProps) {
2525
.sort((nl1, nl2) => nl1.difference - nl2.difference)
2626
.map((nl) => {
2727
return {
28-
key: nl.peak1_id + '_' + nl.peak2_id,
28+
key: nl.peak_id + '_' + nl.precursor_mass,
2929
difference: nl.difference.toFixed(4),
30-
peak1: peaks.find((p) => p.id === nl.peak1_id)?.mz.toFixed(4) ?? '',
31-
peak2: peaks.find((p) => p.id === nl.peak2_id)?.mz.toFixed(4) ?? '',
30+
peak1: peaks.find((p) => p.id === nl.peak_id)?.mz.toFixed(4) ?? '',
31+
peak2: nl.precursor_mass.toFixed(4) ?? '',
3232
} as NeutralLossTableDataType;
3333
}),
3434
[neutralLosses, peaks],
@@ -39,7 +39,12 @@ function NeutralLossTable({ neutralLosses, peaks, width, height }: InputProps) {
3939
setActiveKey(key);
4040
highlightData.dispatch({
4141
type: 'SHOW',
42-
payload: { convertedHighlights: new Set<string>(key.split('_')) },
42+
payload: {
43+
convertedHighlights: new Set<string>([
44+
key.split('_')[0],
45+
'precursor',
46+
]),
47+
},
4348
});
4449
},
4550
[highlightData],
@@ -50,7 +55,12 @@ function NeutralLossTable({ neutralLosses, peaks, width, height }: InputProps) {
5055
setActiveKey(undefined);
5156
highlightData.dispatch({
5257
type: 'HIDE',
53-
payload: { convertedHighlights: new Set<string>(key.split('_')) },
58+
payload: {
59+
convertedHighlights: new Set<string>([
60+
key.split('_')[0],
61+
'precursor',
62+
]),
63+
},
5464
});
5565
},
5666
[highlightData],

web-frontend/src/elements/record/PeakTable.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,9 @@ function PeakTable({ peaks, width, height }: InputProps) {
5252
(p) =>
5353
({
5454
key: p.id,
55-
mz: p.mz ? p.mz.toFixed(4) : 0,
56-
intensity: p.intensity ? p.intensity.toFixed(1) : 0,
57-
rel: p.rel ? p.rel : 0,
55+
mz: p.mz ? p.mz.toFixed(4) : '',
56+
intensity: p.intensity ? p.intensity.toFixed(1) : '',
57+
rel: p.rel ? p.rel : '',
5858
}) as PeakTableDataType,
5959
),
6060
[peaks],

web-frontend/src/elements/routes/pages/search/SpectralHitsViewComponent.tsx

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,22 +27,19 @@ function SpectralHitsViewComponent({
2727
const record = { peak: { peak: { values: reference } } } as Record;
2828
if (hit.peakPairs && hit.peakPairs.length > 0) {
2929
const _neutralLossPeakPairs = hit.peakPairs?.map((nlp) => {
30-
const [peak1_id, peak2_id] = nlp.split('_').map((p) => 'peak-' + p);
31-
return { peak1_id, peak2_id };
30+
const split = nlp.split('_');
31+
return { peakId: 'peak-' + split[0], precursor_mass: Number(split[1]) };
3232
});
3333
const neutralLossData: NeutralLoss[] = [];
3434
for (const nlp of _neutralLossPeakPairs) {
35-
const peak1 = hit.record.peak.peak.values.find(
36-
(p) => p.id === nlp.peak1_id,
35+
const peak = hit.record.peak.peak.values.find(
36+
(p: Peak) => p.id === nlp.peakId,
3737
);
38-
const peak2 = hit.record.peak.peak.values.find(
39-
(p) => p.id === nlp.peak2_id,
40-
);
41-
if (peak1 && peak2) {
42-
const difference = Math.abs(peak1.mz - peak2.mz);
38+
if (peak) {
39+
const difference = Math.abs(peak.mz - nlp.precursor_mass);
4340
neutralLossData.push({
44-
peak1_id: peak1.id,
45-
peak2_id: peak2.id,
41+
peak_id: peak.id,
42+
precursor_mass: nlp.precursor_mass,
4643
difference,
4744
});
4845
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
interface NeutralLoss {
22
difference: number;
3-
peak1_id: string | number;
4-
peak2_id: string | number;
3+
peak_id: string | number;
4+
precursor_mass: number;
55
}
66

77
export default NeutralLoss;

0 commit comments

Comments
 (0)