import | search jupter notebooks for questions and answers | Cell 9 | Search

The code initializes and manages a cache system, loading data from notebooks and creating a database to store cache cells. It also provides a function to interpret queries, searching for files with matching extensions or cells in the cache database as needed.

Run example

npm run import -- "search notebook questions"

search notebook questions

var path = require('path')
var fs = require('fs')

var first = true, cacheCells, cellCache;
// only load caching functions to prevent recursion
async function initializeCache() {
    var {
        getCells, importNotebook, createDatabase
    } = require('../Core')
    var coreNotebooks = [
        path.resolve(__dirname, './files.ipynb'),
        path.resolve(__dirname, './cache.ipynb'),
        path.resolve(__dirname, '../Utilities/levenshtein.ipynb'),
        path.resolve(__dirname, '../Languages/minimatch.ipynb'),
        path.resolve(__dirname, '../Languages/balanced.ipynb'),
    ]
    var cacheLookup = getCells(path.resolve(__dirname, './cache.ipynb'))
        .filter(cell => cell.source.join('').includes('function cacheCells'))
        .map(cell => path.join(__dirname, cell.id))

    cacheCells = importNotebook("cacheLookup").cacheCells
    cellCache = [].concat
        .apply([], coreNotebooks.map(n => cacheCells(n)))
        .map(c => [
            c.mtime,
            path.join(path.dirname(c.filename), c.id),
            c
        ])
    createDatabase(cellCache)

    var {cacheAll} = importNotebook("cache all")
    cacheAll()
    cellCache = importNotebook("cell cache").cellCache

    createDatabase(cellCache)
}

function interpret(queries) {
    var {
        filterLocal, searchFiles, searchQueryFiles,
        queryDatabase, lookupCell
    } = require('../Core')
    if(first) {
        first = false
        initializeCache()
    }

    const fileMode = typeof queries === 'string' && queries.match(/\.ipynb$/ig)
    let searchResults = [].concat.apply([], (typeof queries === 'string'
        ? [queries]
        : queries).map(query => {
        var search = path.basename(query).split(/[\[\]]/ig)
        var searchResults
        if (query.includes('/')) {
            searchResults = filterLocal(query)
        } else if (search.length === 3) {
            searchResults = [searchQueryFiles(search, cellCache)[0]]
        } else if (search[0].match(/\.ipynb$/ig)) {
            searchResults = searchFiles(search[0], cellCache)
        } else {
            searchResults = [queryDatabase(search[0])[0]]
        }
        if(searchResults.length === 0 || typeof searchResults[0] === 'undefined') {
            debugger
            throw new Error(`Nothing found for ${JSON.stringify(query)
                            .substr(0, 200)}`)
        }
        return searchResults
    }))
    if(typeof searchResults[0] === 'undefined') {
        throw new Error(`Can\'t load cells ${JSON.stringify(queries)}`)
    }
    if(typeof searchResults[0].source !== 'undefined') {
        return searchResults
    }
    return typeof queries === 'string' && !fileMode
        ? lookupCell(searchResults[0], cacheCells)
        : searchResults.map(r => lookupCell(r, cacheCells));
}

module.exports.interpret = interpret;

What the code could have been:

const path = require('path');
const fs = require('fs');
const { getCells, importNotebook, createDatabase, filterLocal, searchFiles, searchQueryFiles, queryDatabase, lookupCell } = require('../Core');

const CORE_NOTEBOOKS = [
  path.resolve(__dirname, './files.ipynb'),
  path.resolve(__dirname, './cache.ipynb'),
  path.resolve(__dirname, '../Utilities/levenshtein.ipynb'),
  path.resolve(__dirname, '../Languages/minimatch.ipynb'),
  path.resolve(__dirname, '../Languages/balanced.ipynb'),
];

class Cache {
  constructor() {
    this.cacheCells = null;
    this.cellCache = null;
    this.first = true;
  }

  async initializeCache() {
    if (this.cacheCells) return;
    const coreNotebooks = CORE_NOTEBOOKS.map(n => path.resolve(__dirname, n));
    const cacheLookup = getCells(coreNotebooks.find(n => path.basename(n) === 'cache.ipynb'))
     .filter(cell => cell.source.join('').includes('function cacheCells'))
     .map(cell => path.join(__dirname, cell.id));

    this.cacheCells = await importNotebook(cacheLookup).cacheCells;
    const cellCache = await Promise.all(
      coreNotebooks.map(n => this.cacheCells(n))
    ).then(results => results.map(c => [
      c.mtime,
      path.join(path.dirname(c.filename), c.id),
      c,
    ]));
    await createDatabase(cellCache);

    const { cacheAll } = await importNotebook('cache all');
    await cacheAll();
    this.cellCache = await importNotebook('cell cache').cellCache;

    await createDatabase(this.cellCache);
  }
}

const cache = new Cache();

function interpret(queries) {
  if (cache.first) {
    cache.initializeCache();
    cache.first = false;
  }

  const fileMode = typeof queries ==='string' && queries.match(/\.ipynb$/ig);
  const searchResults = [].concat.apply(
    [],
    (typeof queries ==='string'
     ? [queries]
      : queries).map(query => {
        const search = path.basename(query).split(/[\[\]]/ig);
        let searchResults;

        if (query.includes('/')) {
          searchResults = filterLocal(query);
        } else if (search.length === 3) {
          searchResults = [searchQueryFiles(search, cache.cellCache)[0]];
        } else if (search[0].match(/\.ipynb$/ig)) {
          searchResults = searchFiles(search[0], cache.cellCache);
        } else {
          searchResults = [queryDatabase(search[0])[0]];
        }

        if (!searchResults.length || typeof searchResults[0] === 'undefined') {
          throw new Error(`Nothing found for ${JSON.stringify(query)
           .substr(0, 200)}`);
        }
        return searchResults;
      })
  );

  if (!searchResults.length || typeof searchResults[0] === 'undefined') {
    throw new Error(`Can't load cells ${JSON.stringify(queries)}`);
  }

  if (typeof searchResults[0].source!== 'undefined') {
    return searchResults;
  }

  return typeof queries ==='string' &&!fileMode
   ? lookupCell(searchResults[0], cache.cacheCells)
    : searchResults.map(r => lookupCell(r, cache.cacheCells));
}

module.exports.interpret = interpret;

Code Breakdown

External Dependencies

The code requires three external modules:

Variable Initialization

The code initializes several variables:

initializeCache Function

This function is responsible for initializing the cache. It:

  1. Loads caching functions from ../Core
  2. Specifies a list of core notebooks to load
  3. Retrieves cache cells from the cache.ipynb notebook
  4. Imports notebooks and creates a cache database
  5. Loads the cache all and cell cache notebooks, and updates the cache database

interpret Function

This function handles queries and returns search results. It:

  1. Checks if the first flag is set, and if so, calls initializeCache
  2. Determines the type of query (string or array)
  3. Handles each query:
  4. Returns the search results

Notes