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

Support $search #30

Closed
ekryski opened this issue Feb 8, 2016 · 5 comments
Closed

Support $search #30

ekryski opened this issue Feb 8, 2016 · 5 comments

Comments

@ekryski
Copy link
Member

ekryski commented Feb 8, 2016

This is a proposed special attribute that allows you to fuzzy match a property. Possibly even multiple properties and/or nested documents.

Suggested syntax:

name: {
  $search: ['alice', 'Alice', 'bo', /$bob/i]
}

Following similar syntax to our other special query filters, this would allow you to filter by a singular value, multiple values (treated like an or) and/or regular expressions directly.

For knex we'd have to manually construct OR queries so it would get a little messy because in order to do LIKE queries the syntax is:

knex('users').where('name', 'like', '%Test%');
@daffl
Copy link
Member

daffl commented May 20, 2016

Moved to feathersjs/feathers#334

@daffl daffl closed this as completed May 20, 2016
@strarsis
Copy link

@daffl: As the other issues also was closed, where can I get an example for using a hook to add a q parameter?
I want a text search for the name field - ideally using the q parameter for the item endpoint, as this is what react-admin uses.

@DaddyWarbucks
Copy link

Here are a couple examples of how I typically handle this in both Sequelize and Mongo/Mongoose

Sequelize

export const withSearch = (searchProps, idProps) => (context) => {
  if (!context.params.query) {
    return context;
  }

  const { $search, ...query } = context.params.query;

  if (!$search) {
    context.params.query = query;
    return context;
  }

  const $or = [];

  searchProps.forEach((prop) => {
    $or.push({ [prop]: { $iLike: `%${$search}%` } });
  });

  if (idProps && isValidInteger($search)) {
    idProps.forEach((prop) => {
      $or.push({ [prop]: $search });
    });
  }

  context.params.query = {
    ...query,
    $or: [...(query.$or || []), ...$or]
  };

  return context;
};

Mongo/Mongoose

const traverse = require('traverse');

// https://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex
const escapeRegExp = (string) => {
  return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
};

module.exports.searchRegex = (stringProps, numberProps) => (context) => {
  if (!context.params.query) {
    return context;
  }

  // TODO: Deepclone? Or is traverse immutable?
  const query = { ...context.params.query };
  const $search = query.$search;
  delete query.$search;

  // Find and replace any `$search` properties, even when
  // nested in query or query arrays
  traverse(query).forEach(function (value) {
    if (this.parent && this.parent.node.$search !== undefined) {
      this.parent.node.$regex = new RegExp(escapeRegExp(value), 'i');
      delete this.parent.node.$search;
    }
  });

  if ($search && stringProps) {
    query.$or = query.$or || [];
    stringProps.forEach((prop) => {
      query.$or.push({
        [prop]: { $regex: new RegExp(escapeRegExp($search), 'i') }
      });
    });
  }

  if ($search && !isNaN(Number($search)) && numberProps) {
    query.$or = query.$or || [];
    numberProps.forEach((prop) => {
      query.$or.push({
        [prop]: Number($search)
      });
    });
  }

  context.params.query = query;

  return context;
};

@strarsis
Copy link

Thank you! This can be adapted for Knex.

@strarsis
Copy link

strarsis commented Mar 12, 2022

@DaddyWarbucks: Thanks again!
I made a hook for KnexJS FeathersJS adapter (q to like query):
josx/ra-data-feathers#175 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants