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

Fix upper case querying of jbrowse 1 text search store #3548

Merged
merged 1 commit into from
Feb 24, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -6,59 +6,98 @@ import last from '../../test_data/names/f.json'
import Adapter from './JBrowse1TextSearchAdapter'
import configSchema from './configSchema'

test('adapter can fetch files from names index', async () => {
function mockFetch(url: string): Promise<Response> {
let response = {}
if (url.includes('names/meta.json')) {
response = meta
}
if (url.includes('names/0.json')) {
response = first
}
if (url.includes('names/f.json')) {
response = last
}
return Promise.resolve(new Response(JSON.stringify(response)))
function mockFetch(url: string) {
let response = {}
if (url.includes('names/meta.json')) {
response = meta
}
if (url.includes('names/0.json')) {
response = first
}
if (url.includes('names/f.json')) {
response = last
}
return Promise.resolve(new Response(JSON.stringify(response)))
}

const rootTemplate = path
.join(__dirname, '..', '..', '..', '..', 'test_data', 'names')
.replace(/\\/g, '\\\\')
const rootTemplate = path
.join(__dirname, '..', '..', '..', '..', 'test_data', 'names')
.replace(/\\/g, '\\\\')

test('search upper case', async () => {
const spy = jest.spyOn(global, 'fetch')
// eslint-disable-next-line @typescript-eslint/no-explicit-any
spy.mockImplementation(mockFetch as any)

const urlPath = decodeURI(new URL(`file://${rootTemplate}`).href)
const args = {
type: 'JBrowse1TextSearchAdapter',
textSearchAdapterId: 'JBrowse1GenerateNamesAdapterTest',
namesIndexLocation: {
uri: urlPath,
locationType: 'UriLocation',
},
}
// create adapter
const adapter = new Adapter(configSchema.create(args))
// prefix search
const adapter = new Adapter(
configSchema.create({
type: 'JBrowse1TextSearchAdapter',
textSearchAdapterId: 'JBrowse1GenerateNamesAdapterTest',
namesIndexLocation: {
uri: decodeURI(new URL(`file://${rootTemplate}`).href),
locationType: 'UriLocation',
},
}),
)
const results = await adapter.searchIndex({
searchType: 'prefix',
queryString: 'Apple',
})
// check results are of type BaseResult for prefix search
expect(results.length).toBeGreaterThan(0)
expect(results[0] instanceof BaseResult).toBeTruthy()
expect(results[0].getLabel()).toEqual('Apple1')
expect(results[1].getLabel()).toEqual('Apple2')
expect(results[2].getLabel()).toEqual('Apple3')

// exact search
const results2 = await adapter.searchIndex({
searchType: 'exact',
queryString: 'Apple3',
})
// check results are of type location for exact search
expect(results2.length).toEqual(5)
expect(results2.length).toBeGreaterThan(0)
expect(results2[0] instanceof BaseResult).toBeTruthy()
expect(results2[0].getLabel()).toEqual('Apple3')
expect(results2[0].getLocation()).toEqual('ctgA:17399-23000')
})

test('search lower case', async () => {
const spy = jest.spyOn(global, 'fetch')
// eslint-disable-next-line @typescript-eslint/no-explicit-any
spy.mockImplementation(mockFetch as any)

const adapter = new Adapter(
configSchema.create({
type: 'JBrowse1TextSearchAdapter',
textSearchAdapterId: 'JBrowse1GenerateNamesAdapterTest',
namesIndexLocation: {
uri: decodeURI(new URL(`file://${rootTemplate}`).href),
locationType: 'UriLocation',
},
}),
)
const results = await adapter.searchIndex({
searchType: 'prefix',
queryString: 'apple',
})
// check results are of type BaseResult for prefix search
expect(results.length).toBeGreaterThan(0)
expect(results[0] instanceof BaseResult).toBeTruthy()
expect(results[0].getLabel()).toEqual('Apple1')
expect(results[1].getLabel()).toEqual('Apple2')
expect(results[2].getLabel()).toEqual('Apple3')

// exact search
const results2 = await adapter.searchIndex({
searchType: 'exact',
queryString: 'apple3',
})
// check results are of type location for exact search
expect(results2.length).toEqual(5)
const test2 = results2[0]
expect(test2 instanceof BaseResult).toBeTruthy()
expect(test2.getLabel()).toEqual('Apple3')
expect(test2.getLocation()).toEqual('ctgA:17399-23000')
expect(results2.length).toBeGreaterThan(0)
expect(results2[0] instanceof BaseResult).toBeTruthy()
expect(results2[0].getLabel()).toEqual('Apple3')
expect(results2[0].getLocation()).toEqual('ctgA:17399-23000')
})
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import {
BaseAdapter,
} from '@jbrowse/core/data_adapters/BaseAdapter'
import BaseResult from '@jbrowse/core/TextSearch/BaseResults'
import { Instance } from 'mobx-state-tree'
import { readConfObject } from '@jbrowse/core/configuration'
import MyConfigSchema from './configSchema'
import {
AnyConfigurationModel,
readConfObject,
} from '@jbrowse/core/configuration'
import HttpMap from './HttpMap'
import PluginManager from '@jbrowse/core/PluginManager'
import { getSubAdapterType } from '@jbrowse/core/data_adapters/dataAdapterCache'
Expand All @@ -23,7 +24,8 @@ interface SearchResults {

export type NamesIndexRecord = string | Array<string | number>

// Jbrowse1 text search adapter
type IndexFile = Record<string, SearchResults>

// Uses index built by generate-names.pl
export default class JBrowse1TextSearchAdapter
extends BaseAdapter
Expand All @@ -34,19 +36,15 @@ export default class JBrowse1TextSearchAdapter
tracksNames?: string[]

constructor(
config: Instance<typeof MyConfigSchema>,
config: AnyConfigurationModel,
getSubAdapter?: getSubAdapterType,
pluginManager?: PluginManager,
) {
super(config, getSubAdapter, pluginManager)
const namesIndexLocation = readConfObject(config, 'namesIndexLocation')
if (!namesIndexLocation) {
throw new Error('must provide namesIndexLocation')
}
const namesIndex = readConfObject(config, 'namesIndexLocation')
const { baseUri, uri } = namesIndex
this.httpMap = new HttpMap({
url: namesIndexLocation.baseUri
? new URL(namesIndexLocation.uri, namesIndexLocation.baseUri).href
: namesIndexLocation.uri,
url: baseUri ? new URL(uri, baseUri).href : uri,
})
}

Expand All @@ -55,30 +53,31 @@ export default class JBrowse1TextSearchAdapter
* else it returns empty
* @param query - string query
*/
async loadIndexFile(query: string): Promise<Record<string, SearchResults>> {
async loadIndexFile(query: string): Promise<IndexFile> {
return this.httpMap.getBucket(query)
}

async searchIndex(args: BaseArgs) {
const { searchType, queryString } = args
const tracks = this.tracksNames || (await this.httpMap.getTrackNames())
const entries = await this.loadIndexFile(queryString.toLowerCase())
if (entries[queryString]) {
return this.formatResults(entries[queryString], tracks, searchType)
}
return []
const str = queryString.toLowerCase()
const entries = await this.loadIndexFile(str)
return entries[str]
? this.formatResults(entries[str], tracks, searchType)
: []
}
formatResults(results: SearchResults, tracks: string[], searchType?: string) {
return [
...(searchType === 'exact'
? []
: results.prefix.map(result => {
return new BaseResult({
label: typeof result === 'object' ? result.name : result,
matchedAttribute: 'name',
matchedObject: { result: result },
})
})),
: results.prefix.map(
result =>
new BaseResult({
label: typeof result === 'object' ? result.name : result,
matchedAttribute: 'name',
matchedObject: { result: result },
}),
)),
...results.exact.map(result => {
const name = result[0]
const trackIndex = result[1]
Expand Down