Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add iterative searcher to Foldseek #97

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion backend/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ func server(jobsystem JobSystem, config ConfigRoot) {
var dbs []string
var mode string
var email string
var iterativesearch bool
var taxfilter string

if strings.HasPrefix(req.Header.Get("Content-Type"), "multipart/form-data") {
Expand All @@ -285,6 +286,7 @@ func server(jobsystem JobSystem, config ConfigRoot) {
dbs = req.Form["database[]"]
mode = req.FormValue("mode")
email = req.FormValue("email")
iterativesearch = req.FormValue("iterativesearch") == "true"
taxfilter = req.FormValue("taxfilter")
} else {
err := req.ParseForm()
Expand All @@ -296,6 +298,7 @@ func server(jobsystem JobSystem, config ConfigRoot) {
dbs = req.Form["database[]"]
mode = req.FormValue("mode")
email = req.FormValue("email")
iterativesearch = req.FormValue("iterativesearch") == "true"
taxfilter = req.FormValue("taxfilter")
}

Expand All @@ -315,7 +318,7 @@ func server(jobsystem JobSystem, config ConfigRoot) {
modeWithoutComplex := strings.Join(append(modes[:modeIdx], modes[modeIdx+1:]...), "-")
request, err = NewComplexSearchJobRequest(query, dbs, databases, modeWithoutComplex, config.Paths.Results, email, taxfilter)
} else {
request, err = NewStructureSearchJobRequest(query, dbs, databases, mode, config.Paths.Results, email, taxfilter)
request, err = NewStructureSearchJobRequest(query, dbs, databases, mode, config.Paths.Results, email, iterativesearch, taxfilter)
}
} else {
http.Error(w, "Job type not supported by this server", http.StatusBadRequest)
Expand Down
15 changes: 10 additions & 5 deletions backend/structuresearchjob.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ import (
)

type StructureSearchJob struct {
Size int `json:"size" validate:"required"`
Database []string `json:"database" validate:"required"`
Mode string `json:"mode" validate:"oneof=3di tmalign 3diaa"`
TaxFilter string `json:"taxfilter"`
Size int `json:"size" validate:"required"`
Database []string `json:"database" validate:"required"`
Mode string `json:"mode" validate:"oneof=3di tmalign 3diaa"`
IterativeSearch bool `json:"iterativesearch"`
TaxFilter string `json:"taxfilter"
query string
}

Expand All @@ -22,6 +23,9 @@ func (r StructureSearchJob) Hash() Id {
h.Write(([]byte)(JobStructureSearch))
h.Write([]byte(r.query))
h.Write([]byte(r.Mode))
if r.IterativeSearch {
h.Write([]byte("Iterative"))
}
if r.TaxFilter != "" {
h.Write([]byte(r.TaxFilter))
}
Expand All @@ -48,11 +52,12 @@ func (r StructureSearchJob) WritePDB(path string) error {
return nil
}

func NewStructureSearchJobRequest(query string, dbs []string, validDbs []Params, mode string, resultPath string, email string, taxfilter string) (JobRequest, error) {
func NewStructureSearchJobRequest(query string, dbs []string, validDbs []Params, mode string, resultPath string, email string, iterativeSearch bool, taxfilter string) (JobRequest, error) {
job := StructureSearchJob{
max(strings.Count(query, "HEADER"), 1),
dbs,
mode,
iterativeSearch,
taxfilter,
query,
}
Expand Down
4 changes: 4 additions & 0 deletions backend/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,10 @@ mv -f -- "${BASE}/query.lookup_tmp" "${BASE}/query.lookup"
parameters = append(parameters, "0")
}

if job.IterativeSearch {
parameters = append(parameters, "--num-iterations")
parameters = append(parameters, "3")
}
cmd, done, err := execCommand(config.Verbose, parameters...)
if err != nil {
errChan <- &JobExecutionError{err}
Expand Down
245 changes: 154 additions & 91 deletions frontend/Search.vue
Original file line number Diff line number Diff line change
Expand Up @@ -35,102 +35,147 @@
v-model="query">
</query-textarea>

<div class="actions input-buttons-panel">
<div class="input-buttons-left">
<load-acession-button v-if="$APP == 'foldseek'" @select="query = $event" @loading="accessionLoading = $event" :preload-source="preloadSource" :preload-accession="preloadAccession"></load-acession-button>
<file-button id="file" :label="$STRINGS.UPLOAD_LABEL" v-on:upload="upload"></file-button>
<PredictStructureButton v-if="$APP == 'foldseek'" :query="query" v-model="predictable" v-on:predict="query = $event"></PredictStructureButton>
<file-button id="localFile" label="Upload previous results" @upload="uploadJSON"></file-button>
</div>
</div>
</template>
</panel>
</v-flex>
<v-flex xs12>
<panel collapsible collapsed render-collapsed>
<template slot="header">
<template v-if="!$vuetify.breakpoint.smAndDown">
Databases
</template>
<template v-else>
DBs
</template>
&amp; search settings
</template>
<div slot="content">
<databases
:selected="database"
:all-databases="databases"
@update:selected="database = $event"
@update:all-databases="databases = $event"
:hideEmail="hideEmail"
></databases>
<div class="actions input-buttons-panel">
<div class="input-buttons-left">
<load-acession-button v-if="$APP == 'foldseek'" @select="query = $event" @loading="accessionLoading = $event" :preload-source="preloadSource" :preload-accession="preloadAccession"></load-acession-button>
<file-button id="file" :label="$STRINGS.UPLOAD_LABEL" v-on:upload="upload"></file-button>
<PredictStructureButton v-if="$APP == 'foldseek'" :query="query" v-model="predictable" v-on:predict="query = $event"></PredictStructureButton>
</div>
<file-button id="localFile" label="Upload previous results" @upload="uploadJSON"></file-button>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert unrelated changes

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

</div>
</template>
</panel>
</v-flex>
<v-flex xs12>
<!-- Databases Section -->
<panel collapsible collapsed>
<template slot="header">
<template v-if="!$vuetify.breakpoint.smAndDown">
Databases
</template>
<template v-else>
DBs
</template>
</template>
<div slot="content">
<!-- Existing Database Selection -->
<div class="input-group">
<v-tooltip open-delay="300" top>
<template v-slot:activator="{ on }">
<label v-on="on">Databases&nbsp;<v-icon color="#FFFFFFB3" style="margin-top:-3px" small v-on="on">{{ $MDI.HelpCircleOutline }}</v-icon></label>
</template>
<span v-if="$ELECTRON || hideEmail">Choose the databases to search against and the result mode.</span>
<span v-else>Choose the databases to search against, the result mode, and optionally an email to notify you when the job is done.</span>
</v-tooltip>
</div>

<template v-if="databases.length > 0">
<v-checkbox v-for="(db, index) in databases" v-model="database" :key="index" :value="db.path" :label="db.name + ' ' + db.version" :append-icon="(db.status == 'ERROR' || db.status == 'UNKNOWN') ? $MDI.AlertCircleOutline : ((db.status == 'PENDING' || db.status == 'RUNNING') ? $MDI.ProgressWrench : undefined)" :disabled="db.status != 'COMPLETE'" hide-details></v-checkbox>
</template>

<div v-if="databasesNotReady" class="alert alert-info mt-1">
<span>Databases are loading...</span>
</div>
</div>
</panel>

<v-radio-group v-model="mode">
<v-tooltip open-delay="300" top>
<template v-slot:activator="{ on }">
<label v-on="on">Mode&nbsp;<v-icon color="#FFFFFFB3" style="margin-top:-3px" small v-on="on">{{ $MDI.HelpCircleOutline }}</v-icon></label>
<!-- Search Parameters Section -->
<panel collapsible collapsed>
<template slot="header">
<template v-if="!$vuetify.breakpoint.smAndDown">
Search Parameters
</template>
<template v-else>
Params
</template>
</template>
<span v-html="$STRINGS.MODE_HELP"></span>
</v-tooltip>
<v-radio hide-details
v-for="i in ($STRINGS.MODE_COUNT - 0)"
:key="i"
:value="$STRINGS['MODE_KEY_' + i]"
:label="$STRINGS['MODE_TITLE_' + i]"
></v-radio>
</v-radio-group>
<div slot="content">
<!-- Mode Section -->
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert unrelated changes

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

<v-radio-group v-model="mode">
<v-tooltip open-delay="300" top>
<template v-slot:activator="{ on }">
<label v-on="on">Mode&nbsp;<v-icon color="#FFFFFFB3" style="margin-top:-3px" small v-on="on">{{ $MDI.HelpCircleOutline }}</v-icon></label>
</template>
<span v-html="$STRINGS.MODE_HELP"></span>
</v-tooltip>
<v-radio hide-details
v-for="i in ($STRINGS.MODE_COUNT - 0)"
:key="i"
:value="$STRINGS['MODE_KEY_' + i]"
:label="$STRINGS['MODE_TITLE_' + i]">
</v-radio>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert the whitespace changes

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

</v-radio-group>

<TaxonomyAutocomplete v-model="taxFilter"></TaxonomyAutocomplete>
<TaxonomyAutocomplete v-model="taxFilter"></TaxonomyAutocomplete>

<v-tooltip v-if="!$ELECTRON && !hideEmail" open-delay="300" top>
<template v-slot:activator="{ on }">
<v-text-field v-on="on" label="Notification Email (Optional)" placeholder="[email protected]" v-model="email"></v-text-field>
</template>
<span>Send an email when the job is done.</span>
</v-tooltip>
</div>
</panel>
</v-flex>
<v-flex>
<panel>
<template slot="content">
<div class="actions" :style="!$vuetify.breakpoint.xsOnly ?'display:flex; align-items: center;' : null">
<v-item-group class="v-btn-toggle">
<v-btn color="primary" :block="false" x-large v-on:click="search" :disabled="searchDisabled"><v-icon>{{ $MDI.Magnify }}</v-icon>&nbsp;Search</v-btn>
<v-btn v-if="isMultimer" color="secondary" :block="false" x-large v-on:click="goToMultimer"><v-icon>{{ $MDI.Multimer }}</v-icon>&nbsp;Go to Multimer</v-btn>
</v-item-group>
<div :style="!$vuetify.breakpoint.xsOnly ? 'margin-left: 1em;' : 'margin-top: 1em;'">
<span><strong>Summary</strong></span><br>
Search <template v-if="taxFilter">
<strong>{{ taxFilter.text }}</strong> in
</template>
<template v-if="database.length == databases.length">
<strong>all available</strong> databases
<v-divider class="my-2"></v-divider>

<v-radio-group v-model="iterativeSearch">
<v-tooltip open-delay="300" top>
<template v-slot:activator="{ on }">
<label v-on="on">
Iterative search
<v-icon color="#FFFFFFB3" style="margin-top:-3px" small v-on="on">{{ $MDI.HelpCircleOutline }}</v-icon>
</label>
</template>
<span>Improve sensitivity of search by performing an iterative search (--num-iterations 3).</span>
</v-tooltip>

<v-radio label="On" :value="true"></v-radio>
<v-radio label="Off" :value="false"></v-radio>
</v-radio-group>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please keep email in addition to the new iterative search

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

</div>
</panel>


Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove empty newlines

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

</v-flex>
<v-flex>
<panel>
<template slot="content">
<div class="actions" :style="!$vuetify.breakpoint.xsOnly ?'display:flex; align-items: center;' : null">
<v-btn color="primary" :block="$vuetify.breakpoint.xsOnly" x-large v-on:click="search" :disabled="searchDisabled"><v-icon>{{ $MDI.Magnify }}</v-icon>&nbsp;Search</v-btn>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please restore previous changes

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

<div :style="!$vuetify.breakpoint.xsOnly ? 'margin-left: 1em;' : 'margin-top: 1em;'">
<span><strong>Summary</strong></span><br>
Search <template v-if="taxFilter">
<strong>{{ taxFilter.text }}</strong> in
</template>
<template v-if="database.length == databases.length">
<strong>all available</strong> databases
</template>
<template v-else>
<strong>{{ database.length }}</strong>
<template v-if="database.length == 1">
database
</template>
<template v-else>
databases
</template>
({{
databases.filter(db => database.includes(db.path)).map(db => db.name).sort().join(", ")
}})
</template> with {{ $STRINGS.APP_NAME }} in <strong>{{ modes[mode] }}</strong> mode<span v-if="iterativeSearch"><strong> iterative</strong></span>.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add iterative to beginning of sentence

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why?

<div v-if="errorMessage != ''" class="v-alert v-alert--outlined warning--text mt-2">
<span>{{ errorMessage }}</span>
</div>
</div>
</div>
</template>
<template v-else>
<strong>{{ database.length }}</strong>
<template v-if="database.length == 1">
database
</template>
<template v-else>
databases
</template>
({{
databases.filter(db => database.includes(db.path)).map(db => db.name).sort().join(", ")
}})
</template> with {{ $STRINGS.APP_NAME }} in <strong>{{ modes[mode] }}</strong> mode.
<div v-if="errorMessage != ''" class="v-alert v-alert--outlined warning--text mt-2">
<span>{{ errorMessage }}</span>
</div>
</div>
</div>
</template>
</panel>
</panel>
</v-flex>
</v-layout>
<v-layout wrap>
<v-flex xs12>
<v-card rounded="0">
<v-card-title primary-title class="pb-0 mb-0">
<div class="text-h5 mb-0">Reference</div>
</v-card-title>
<v-card-title primary-title class="pt-0 mt-0">
<p class="text-subtitle-2 mb-0" v-html="$STRINGS.CITATION"></p>
</v-card-title>
</v-card>
</v-flex>
</v-layout>
<reference :reference="$STRINGS.CITATION"></reference>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please restore the previous version

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

</v-container>
</v-container>
</template>

<script>
Expand Down Expand Up @@ -168,14 +213,16 @@ export default {
return {
inSearch: false,
errorMessage: "",
mode: storage.getItem('mode') || this.$STRINGS.MODE_DEFAULT_KEY,
showCurl: false,
mode: this.$STRINGS.MODE_DEFAULT_KEY,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please restore the previous version

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

modes: Array.from({length: this.$STRINGS.MODE_COUNT - 0}, (_, i) => i + 1)
.reduce((dict, i, _) => { dict[this.$STRINGS['MODE_KEY_' + i]] = this.$STRINGS['MODE_TITLE_' + i]; return dict; }, {}),
email: storage.getItem('email') || "",
hideEmail: true,
query: "",
database: JSON.parse(storage.getItem('database') || '[]'),
databases: JSON.parse(storage.getItem('databases') || '[]'),
iterativeSearch: JSON.parse(storage.getItem('iterativeSearch') || false),
taxFilter: JSON.parse(storage.getItem('taxFilter') || 'null'),
predictable: false,
accessionLoading: false,
Expand All @@ -193,6 +240,18 @@ export default {
} else {
this.query = this.$STRINGS.QUERY_DEFAULT;
}
if (localStorageEnabled && localStorage.database) {
this.database = JSON.parse(localStorage.database);
}
if (localStorageEnabled && localStorage.databases) {
this.databases = JSON.parse(localStorage.databases);
}
if (localStorageEnabled && localStorage.iterativeSearch) {
this.iterativeSearch = JSON.parse(localStorage.iterativeSearch);
}
if (localStorageEnabled && localStorage.taxFilter) {
this.taxFilter = JSON.parse(localStorage.taxFilter);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These should be set somewhere else, they were removed in the last refactoring

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

},
computed: {
searchDisabled() {
Expand Down Expand Up @@ -235,6 +294,9 @@ export default {
databases(value) {
storage.setItem('databases', JSON.stringify(value));
},
iterativeSearch(value) {
storage.setItem('iterativeSearch', JSON.stringify(value));
},
taxFilter(value) {
storage.setItem('taxFilter', JSON.stringify(value));
},
Expand All @@ -245,7 +307,8 @@ export default {
q: this.query,
database: this.database,
mode: this.mode,
email: this.email
email: this.email,
iterativesearch: this.iterativeSearch
};
if (__APP__ == "foldseek" && typeof(request.q) === 'string' && request.q != '') {
if (request.q[request.q.length - 1] != '\n') {
Expand Down