npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

searchstring

v2.0.1

Published

parse a string into JSON-based search query

Downloads

129

Readme

Search String

Parse a search string into list of conditions, for parsing simple combinations of search conditions.

const searchstring = require("searchstring");
const conds = searchstring("color:black type:'small box' size:>5 !'evil'");
conds[0];// {value: "black", prop: "color"}
conds[1];// {value: "small box", prop: "type"}
conds[2];// {value: 5, prop: "size", op: ">"}
conds[3];// {value: "evil", op: "!"}

Note about escaping (how many backslashes do I need?)

In this Markdown file, there are extra backslashes to make the HTML documentation display correctly. If you are reading README.md "directly" in your text editor, you should interpret each double-backslash as a single backslash!

Parsing

The search string is a series of space-separated terms. A term consists of:

  • (optional) property name followed by a colon (":"), possibly quoted. e.g.:
    • myProperty:
    • 'spaced property':
  • (optional) operator, any of: !, =, <=, <, >=, >
  • value, possibly quoted, e.g.:
    • aStringValue
    • "a string value",
    • 'singled-quote value'
    • 'it\'s with an escaped quote'

The value returned is parsed into a number if it looks like a number.

Quoting & Escaping

Both property names and values may be quoted. This allows you to keep spaces, whereas a space normally move the parser on to the next property/operator/value). You can use single or double quotes:

  • 'the same thing'
  • "the same thing"

What if you need to include a quote in a value? You can either use the "other" type of quote, or use the backslash to escape the next character. In fact, the backslash always escapes the next character, even outside of quotes:

  • "it's the same"
  • 'it\'s the same'
  • it\'s\ the\ same

All of these variants are handled only because humans the world over agree that backslashes should be used in moderation and when absolutely necessary.

Using the Result

const searchstring = require("searchstring");
let result = searchstring(myStringFromSomewhere);

The result is an an array of parsed property/operator/value objects. Each object may have the following properties:

  • value: the mandatory value portion, quotes and escapes handled
  • op: if operator used, this is a literal string containing the operator
  • prop: if property name was used, this is is set to it

Examples

What you do further with this is up to you.

One possibility would be, for example, to run a database query where all the given conditions are satisfied. In this scenario, you could search particular fields (say, indexed ones), when there is no "prop" available. Otherwise, you could limit the search to the given field.

In this example we are translating the searchstring results into a MongoDB query.

function mongoQueryFromSearchString (ss, indexedFields, availableFields) {
  if (!ss.length) { throw new Error('no search terms!') }
  const andQuery = []
  for (let res of ss) {
    if (!res.prop) {
      const orQuery = []
      for (let field of indexedFields) {
        orQuery.push(getQuery(res.value, res.op, field))
      }
      andQuery.push({$or: orQuery})
    } else if (availableFields.indexOf(res.prop) !== -1) {
      andQuery.push(getQuery(res.value, res.op, res.prop))
    } else { // given a property not searchable
      console.error(`User searched on unavailable property ${res.prop}`)
    }
  }
  if (!andQuery.length) { throw new Error('no usable filter!') }
  return {$and: andQuery}
}

function getQuery (value, op, field) {
  const q = {}
  switch (op) {
    case '!': q[field] = {$not: value}; break
    case '<': q[field] = {$lt: value}; break
    case '<=': q[field] = {$lte: value}; break
    case '>': q[field] = {$gt: value}; break
    case '>=': q[field] = {$gte: value}; break
    case '=':
    default: q[field] = value
  }
  return q
}

let query = mongoQueryFromSearchString(searchstring(queryString, ixFields, avFields))
database.collection.find(query).then((documents) => {
  sendDocumentsToCliBecauseEveryoneUsesCli(documents)
})

TODO

Document value as RegExp (and support flags).