-
-
Notifications
You must be signed in to change notification settings - Fork 751
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 in query syntax #334
Comments
any news on this feature ? |
You can create a hook that for e.g. MongoDB converts the app.service('todos').before({
find(hook) {
const query = hook.params.query;
if(query.name.$search) {
query.name = { $regex: new RegExp(query.name.$search) }
}
}
}); |
the way to make fuzzy match for some field in collection before your comment was to use custom route and write native query with same service db engine like "mongo" or "nedb" . |
what about this hook as simple solution for search in mongodb & nedb exports.searchRegex = function () {
return function (hook) {
const query = hook.params.query;
for (let field in query) {
if(query[field].$search && field.indexOf('$') == -1) {
query[field] = { $regex: new RegExp(query[field].$search) }
}
}
hook.params.query = query
return hook
}
} and simply include it in the src/services/ServiceName/hooks like exports.before = {
all: [],
find: [globalHooks.searchRegex()]
} |
For those interested, you can make the above code posted by @alnour-altegani case insensitive by changing query[field] = { $regex: new RegExp(query[field].$search) } to: query[field] = { $regex: new RegExp(query[field].$search, 'i') } // note the 'i' |
so why $search, not $regex? |
Just stumbled upon this after fighting with the Mongoose adapter for half an hour. This would be a super nice addition. Any suggestions for a work-around for the |
There is a working hook two comments above. Mongoose already supports it, you just have to convert the query into a regular expression in a By now, I am also leaning more towards not adding this. Defining an abstract search format for all databases isn't really possible. Some support |
Thanks for the quick reply. |
got it, my fault. Thanks my solution with implementation of $or for multiple $search exports.searchRegex = function () {
return function (hook) {
const query = hook.params.query;
for (let field in query) {
if(query[field].$search && field.indexOf('$') == -1) {
query[field] = { $regex: new RegExp(query[field].$search, 'i') }
}
if(field == '$or') {
query[field].map((action, index) => {
let f = Object.keys(action)[0];
if(action[f].$search) {
action[f] = { $regex: new RegExp(action[f].$search, 'i') }
}
return action;
});
}
}
hook.params.query = query
return hook
}
} But how you guys deal with integer? |
@sajov thank you for sharing your work . but, can you give example of how to use this code to search with $or operator ? |
@ruddfawcett i use it in a datatable context like this for (var i = 0;i < opts.tableHeader.length; i++) {
let q = {};
q[opts.tableHeader[i]] = {$search: queryObj.search.value.value};
query.$or.push(q);
} here is an working example https://github.com/sajov |
@sajov Were you able to write a README ? I'd be very interested to see how I could use this case in combination with an
|
Hook example which treat Client example query.$or = [
{fieldA : {$search: value}},
{fieldB : {$search: value}},
]; hope that answer your question |
@sajov Thanks! I rewrote things a bit and ended up using the hook below for a client side query containing this:
Hook:
Suggestions welcome of course |
Maybe @eddyystop has some thoughts if this can be turned into a common hook. |
Just saw this now. I've created a link in feathers-hooks-common feathersjs-ecosystem/feathers-hooks-common#141 |
Here is a fuzzy match for NeDB, it searches all properties case insensitive: module.exports = function (options = {}) { // eslint-disable-line no-unused-vars
return function (hook) {
if (hook.params.query && hook.params.query.$search) {
hook.params.query.$where = fuzzySearch(hook.params.query.$search)
delete hook.params.query.$search
}
return hook
}
}
/**
* Returns a $where function for NeDB. The function search all
* properties of objects and returns true if `str` is found in
* one of the properties. Searching is not case sensitive.
*
* @param {string} str search for this string
* @return {function}
*/
function fuzzySearch (str) {
let r = new RegExp(str, 'i')
return function () {
for (let key in this) {
// do not search _id and similar fields
if (key[0] === '_' || !this.hasOwnProperty(key)) {
continue
}
if (this[key].match(r)) {
return true
}
}
return false
}
} I actually found this to be a bit quicker than single property regex. With fuzzy search I got 47 ms average time for |
Based on my comment above I've made to plugins: |
Ref feathersjs/feathers#334: > We will add documentation for searching to the adapters individually.
* Add note about searching documents Ref feathersjs/feathers#334: > We will add documentation for searching to the adapters individually. * add REST example to $search section
Hi, stumbled into this thread from querying.md: ... which is referring here. Noticed a little mistake in the querying sample URI for the $in,$nin example. If I am not mistaken it should be: instead of: because $in is an array in the query. |
Hi Ulrich,
get parsed by qs into
see the option |
@sajov Thank you for the hook. Btw, what is |
Hi everybody! I'm trying to use feathers-nedb-fuzzy-search
What's wrong with my Feathers/NeDB? |
In the latest versions of all database adapters non-standard query parameters have to be explicitly whitelisted. nedbService({
whitelist: [ '$where', '$search' ]
}); |
The whitelisting seems to break feathers-vuex since it uses commons. Not related to this thread tho. I'll dig deeper on that and log an issue over there. |
This from ghost works for me. |
While using Then you can try
|
This is an ALL inclusive search - meaning it looks for a match in all fields:
This will match any of the fields with $or: exports.searchRegEx = function () {
return function (hook) {
const query = hook.params.query;
for (let field in query) {
if(query[field].$search && field.indexOf('$') == -1) {
query.$or = [...(query.$or || []), { [field]: { $regex: new RegExp(query[field].$search, 'ig') } }];
delete query[field];
}
}
hook.params.query = query
return hook
}
}
` |
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;
}; The Sequelize example is pretty similar to many I have seen here already, except it handles Integer fields as well. The Mongo/Mongoose example is better because it uses |
I am consolidating all the individual issues and discussions here:
This is a proposed special attribute that allows you to fuzzy match a property. Possibly even multiple properties and/or nested documents.
Suggested syntax:
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.
Service Adapter Completion
The text was updated successfully, but these errors were encountered: