openchemlib-utils
Advanced tools
Comparing version 2.1.3 to 2.2.0
{ | ||
"name": "openchemlib-utils", | ||
"version": "2.1.3", | ||
"version": "2.2.0", | ||
"description": "Various utilities that extends openchemlib-js like HOSE codes or diastereotopic IDs", | ||
@@ -33,13 +33,13 @@ "main": "lib/index.js", | ||
"devDependencies": { | ||
"@babel/plugin-transform-modules-commonjs": "^7.19.6", | ||
"@types/jest": "^29.2.4", | ||
"@babel/plugin-transform-modules-commonjs": "^7.20.11", | ||
"@types/jest": "^29.2.5", | ||
"cheminfo-build": "^1.1.11", | ||
"eslint": "^8.29.0", | ||
"eslint": "^8.32.0", | ||
"eslint-config-cheminfo": "^8.1.3", | ||
"eslint-plugin-import": "^2.26.0", | ||
"eslint-plugin-import": "^2.27.5", | ||
"jest": "^29.3.1", | ||
"jest-matcher-deep-close-to": "^3.0.2", | ||
"openchemlib": "8.0.0", | ||
"prettier": "^2.8.1", | ||
"rollup": "^3.7.4" | ||
"prettier": "^2.8.3", | ||
"rollup": "^3.10.0" | ||
}, | ||
@@ -46,0 +46,0 @@ "dependencies": { |
@@ -7,3 +7,3 @@ import appendCSV from './utils/appendCSV'; | ||
import pushMoleculeInfo from './utils/pushMoleculeInfo'; | ||
import search from './utils/search'; | ||
import { search, searchAsync } from './utils/search'; | ||
/* | ||
@@ -21,3 +21,3 @@ this.db is an object with properties 'oclID' that has as value | ||
* | ||
* @param {OCL} [OCL] The openchemlib library | ||
* @param {import('openchemlib')} OCL - openchemlib library | ||
* @param {object} [options={}] | ||
@@ -120,2 +120,21 @@ * @param {boolean} [options.computeProperties=false] | ||
/** | ||
* Search in a MoleculesDB | ||
* Inside the database all the same molecules are group together | ||
* @param {string|OCL.Molecule} [query] smiles, molfile, oclCode or instance of Molecule to look for | ||
* @param {object} [options={}] | ||
* @param {string} [options.format='idCode'] - query is in the format 'smiles', 'oclid' or 'molfile' | ||
* @param {string} [options.mode='substructure'] - search by 'substructure', 'exact' or 'similarity' | ||
* @param {boolean} [options.flattenResult=true] - The database group the data for the same product. This allows to flatten the result | ||
* @param {boolean} [options.keepMolecule=false] - keep the OCL.Molecule object in the result | ||
* @param {number} [options.limit=Number.MAX_SAFE_INTEGER] - maximal number of result | ||
* @param {number} [options.interval=100] - interval in ms to call the onStep callback | ||
* @param {function} [options.onStep] - callback to execute after each interval | ||
* @param {AbortController} [options.controler] - callback to execute to check if the search should be aborted | ||
* @return {Promise<Array>} array of object of the type {(molecule), idCode, data, properties} | ||
*/ | ||
searchAsync(query, options) { | ||
return searchAsync(this, query, options); | ||
} | ||
/** | ||
* Returns an array with the current database | ||
@@ -122,0 +141,0 @@ * @returns |
@@ -0,12 +1,13 @@ | ||
import { noWait } from '../../util/noWait.js'; | ||
import getMoleculeCreators from './getMoleculeCreators'; | ||
export default function search(moleculesDB, query = '', options = {}) { | ||
const { | ||
format = 'idCode', | ||
mode = 'substructure', | ||
flattenResult = true, | ||
keepMolecule = false, | ||
limit = Number.MAX_SAFE_INTEGER, | ||
} = options; | ||
class AbortError extends Error { | ||
name = 'AbortError'; | ||
code = 20; | ||
} | ||
function getQuery(moleculesDB, query, options) { | ||
const { format = 'idCode' } = options; | ||
if (typeof query === 'string') { | ||
@@ -18,3 +19,10 @@ const moleculeCreators = getMoleculeCreators(moleculesDB.OCL.Molecule); | ||
} | ||
return query; | ||
} | ||
export function search(moleculesDB, query = '', options = {}) { | ||
const { mode = 'substructure' } = options; | ||
query = getQuery(moleculesDB, query, options); | ||
let result; | ||
@@ -34,5 +42,27 @@ switch (mode.toLowerCase()) { | ||
} | ||
return processResult(result, { flattenResult, keepMolecule, limit }); | ||
return processResult(result, options); | ||
} | ||
export async function searchAsync(moleculesDB, query = '', options = {}) { | ||
const { mode = 'substructure' } = options; | ||
query = getQuery(moleculesDB, query, options); | ||
let result; | ||
switch (mode.toLowerCase()) { | ||
case 'exact': | ||
result = exactSearch(moleculesDB, query); | ||
break; | ||
case 'substructure': | ||
result = await subStructureSearchAsync(moleculesDB, query, options); | ||
break; | ||
case 'similarity': | ||
result = similaritySearch(moleculesDB, query); | ||
break; | ||
default: | ||
throw new Error(`unknown search mode: ${options.mode}`); | ||
} | ||
return processResult(result, options); | ||
} | ||
function exactSearch(moleculesDB, query) { | ||
@@ -46,3 +76,3 @@ const queryIDCode = query.getIDCode(); | ||
function subStructureSearch(moleculesDB, query) { | ||
function substructureSearchBegin(moleculesDB, query) { | ||
let resetFragment = false; | ||
@@ -60,6 +90,29 @@ if (!query.isFragment()) { | ||
} | ||
} else { | ||
} | ||
return { resetFragment, queryMW, searchResult }; | ||
} | ||
function substructureSearchEnd(searchResult, queryMW, resetFragment, query) { | ||
searchResult.sort((a, b) => { | ||
return ( | ||
Math.abs(queryMW - a.properties.mw) - Math.abs(queryMW - b.properties.mw) | ||
); | ||
}); | ||
if (resetFragment) { | ||
query.setFragment(false); | ||
} | ||
return searchResult; | ||
} | ||
function subStructureSearch(moleculesDB, query) { | ||
const { resetFragment, queryMW, searchResult } = substructureSearchBegin( | ||
moleculesDB, | ||
query, | ||
); | ||
if (searchResult.length === 0) { | ||
const queryIndex = query.getIndex(); | ||
const searcher = moleculesDB.searcher; | ||
searcher.setFragment(query, queryIndex); | ||
@@ -75,13 +128,51 @@ for (let idCode in moleculesDB.db) { | ||
searchResult.sort((a, b) => { | ||
return ( | ||
Math.abs(queryMW - a.properties.mw) - Math.abs(queryMW - b.properties.mw) | ||
); | ||
}); | ||
return substructureSearchEnd(searchResult, queryMW, resetFragment, query); | ||
} | ||
if (resetFragment) { | ||
query.setFragment(false); | ||
async function subStructureSearchAsync(moleculesDB, query, options = {}) { | ||
const { interval = 100, onStep, controller } = options; | ||
let shouldAbort = false; | ||
if (controller) { | ||
const abortEventListener = () => { | ||
shouldAbort = true; | ||
}; | ||
controller.signal.addEventListener('abort', abortEventListener); | ||
} | ||
return searchResult; | ||
const { resetFragment, queryMW, searchResult } = substructureSearchBegin( | ||
moleculesDB, | ||
query, | ||
); | ||
let begin = performance.now(); | ||
if (searchResult.length === 0) { | ||
const queryIndex = query.getIndex(); | ||
const searcher = moleculesDB.searcher; | ||
searcher.setFragment(query, queryIndex); | ||
let index = 0; | ||
let length = Object.keys(moleculesDB.db).length; | ||
for (let idCode in moleculesDB.db) { | ||
if (shouldAbort) { | ||
throw new AbortError('Query aborted'); | ||
} | ||
let entry = moleculesDB.db[idCode]; | ||
searcher.setMolecule(entry.molecule, entry.index); | ||
if (searcher.isFragmentInMolecule()) { | ||
searchResult.push(entry); | ||
} | ||
if ((onStep || controller) && performance.now() - begin >= interval) { | ||
begin = performance.now(); | ||
if (onStep) { | ||
onStep(index, length); | ||
} | ||
if (controller && !onStep) { | ||
await noWait(); | ||
} | ||
} | ||
index++; | ||
} | ||
} | ||
return substructureSearchEnd(searchResult, queryMW, resetFragment, query); | ||
} | ||
@@ -88,0 +179,0 @@ |
@@ -7,3 +7,3 @@ import { getDiastereotopicAtomIDsAndH } from './getDiastereotopicAtomIDsAndH.js'; | ||
* Pelase take care than numbering of atoms starts at 0 ! | ||
* @param {object} OCL - openchemlib library | ||
* @param {import('openchemlib')} OCL - openchemlib library | ||
* @param {string} molfile | ||
@@ -10,0 +10,0 @@ * @returns |
@@ -7,3 +7,3 @@ const MAX_R = 10; | ||
* @param {array} [fragments] Array of {smiles,R1,R2,...} | ||
* @param {OCL} [OCL] The openchemlib library | ||
* @param {import('openchemlib')} OCL - openchemlib library | ||
* @param {object} [options={}] | ||
@@ -10,0 +10,0 @@ * @param {function} [options.onStep] method to execute each new molecules |
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
153056
52
4303