I want to implement .test file for the following function:
import Fuse from 'fuse.js';
import data from '../dal/data';
async function search(searchedData) {
// In case searched name is null
if (!searchedData) {
return [];
}
// load data
const dataload = await data();
// set options to filter data
const options = {
includeMatches: true,
// Search in `name'
keys: ['name'],
};
const fuse = new Fuse(dataload, options);
const matchedList = fuse.search(searchedData)
.map((element) => element.item);
return { matchedList };
}
export default search;
How to do for Fuse.js, shall I implement a mock data?
Fuse.js is library that provides fuzzy search based on dynamic configurations pass to it. There are different library which are available such as fuzzy-search.
Now, the question is how you can mock similar library as fuzzy search as per the comment. This is a good example of how you can start building your own fuzzy search engine, but to do so, you need to understand what fuzzy search is and how it works.
Now, if you want to know how Fuse.js works with any mock data. I can add some code which might help you to integrate with your existing project.
Mock Data
// it could be of any type, I am using Array of objects
const mockData = [{
name : 'Jason Durham',
age : 28,
address : 'Newyork city, New York, USA'
}, {
name : 'Jameson Durham',
age : 28,
address : 'Cleveland, Ohio, USA'
},
{
name : 'Tintan Groofs',
age : 28,
address : 'Ohio city, Ohio, USA'
}]
const options = {
includeScore: true, // the o/p will include the ranking based on weights
keys: [
{
name: 'name',
weight: 0.3 // what weightage you want this key in your search result
},
{
name: 'address',
weight: 0.7 // what weightage you want this key in your search result
}
]
}
// Create a new instance of Fuse
const fuse = new Fuse(mockData, options)
// Now search for 'Man'
const result = fuse.search('nwyork');
console.log(result);
You can check fuse.js documentation to check how to advance your search and configurable.
You can test the fuzzy search results using a test framework provided that you know the expected values, but it will contain a very broad set of o/p based on your configuration. As of now Fuse.js doesn't provide direct way in their documentation to check for it, but you can create a method based on the ranking you get and create a threshold value for it. This is the way we are testing our search results using Fuse.js, all the results below this threshold is discarded.
I'm using mongoose 5.11.4 and trying to find documents. On the official docs they say
MyModel.find({ name: /john/i })
I wanna know how to use variables for "name" & "john" and get the exact thing done. this is an API that I'm working on. filter (name) and the value (john) gonna decide by the frontend user. We should search for a given field using the value. Any suggestions?
let filter = req.params.filter
let value = req.params.value
MyModel.find({ filter : /value/i })
doesn't work
You can use the RegExp object to convert the value to a regular expression.
let regex = new RegExp(`${value}`,'options');
Now you can use it on mongoose query.
model.find({name:regex});
this worked : )
let filter = req.params.filter
let value = req.params.value
const filterParam = {}
filterParam[filter] = { $regex: .*${value}.* }
const suppliers = await Supplier.find(filterParam)
I'm currently writing a small API for a cooking app. I have a Recipe model and would like to implement sorting by columns based on the req Parameter given.
I'd like to sort by whatever is passed in the api call. the select parameter works perfectly fine, I can select the columns to be displayed but when I try to sort anything (let's say by rating) the return does sort but I'm not sure what it does sort by.
The code i'm using:
query = Recipe.find(JSON.parse(queryStr));
if(req.query.select){
const fields = req.query.select.split(',').join(' ');
query = query.select(fields);
}
if(req.query.sort){
const sortBy = req.query.sort.split(',').join(' ');
query = query.sort({ sortBy: 1 });
} else {
query = query.sort({ _id: -1 });
}
The result, when no sorting is set: https://pastebin.com/rPLv8n5s
vs. the result when I pass &sort=rating: https://pastebin.com/7eYwAvQf
also, when sorting my name the result is also mixed up.
You are not using the value of sortBy but the string "sortBy". You will need to create an object that has the rating as an object key.
You need the sorting object to look like this.
{
rating: 1
}
You can use something like this so it will be dynamic.
if(req.query.sort){
const sortByKey = req.query.sort.split(',').join(' ');
const sortByObj = {};
sortByObj[sortByKey] = 1; // <-- using sortBy as the key
query = query.sort(sortByObj);
} else {
query = query.sort({ _id: -1 });
}
Is it possible to use the nextval function in a pg-promise multirow insert?
I have a database (that I sadly can't change) where the id has to be inserted via the client like this:
INSERT INTO some_object (object_id, object_name)
VALUES (nextval('some_object_seq'), ${object_name})
RETURNING object_id;
This works fine for one insert. But now I have to insert multiple rows at once and tried pgp.helpers.insert:
const cs = pgp.helpers.ColumnSet(['object_id', 'object_name'], { table });
const query = pgp.helpers.insert(values, cs) + 'RETURNING object_id';
database.many(query).then(data => {
return data
}).catch(error => {
logger.error(error, query);
});
Is there any way to use nextval('some_object_seq') in this scenario? Sadly I can't change the default value of the id column in the table definition.
Your column should be defined as this:
{
name: `object_id`,
init: () => `nextval('some_object_seq')`,
mod: `:raw`
}
As opposed to the answer by #baal, you do not need to use def, because you are not providing a default value, rather a complete override for the value, which is what init for.
And it can be used within upsert queries too.
As Bergi wrote, it is possible to add a default expression to the column set like this:
const cs = pgp.helpers.ColumnSet(
[{
name: "object_id",
// setting the DEFAULT value for this column
def: "nextval('some_object_seq')",
// use raw-text modifier to inject string directly
mod: "^",
}, 'object_name'], { table });
From the docs:
You can also chain multiple where() methods to create more specific queries (logical AND).
How can I perform an OR query?
Example:
Give me all documents where the field status is open OR upcoming
Give me all documents where the field status == open OR createdAt <= <somedatetime>
OR isn't supported as it's hard for the server to scale it (requires keeping state to dedup). The work around is to issue 2 queries, one for each condition, and dedup on the client.
Edit (Nov 2019):
Cloud Firestore now supports IN queries which are a limited type of OR query.
For the example above you could do:
// Get all documents in 'foo' where status is open or upcmoming
db.collection('foo').where('status','in',['open','upcoming']).get()
However it's still not possible to do a general OR condition involving multiple fields.
With the recent addition of IN queries, Firestore supports "up to 10 equality clauses on the same field with a logical OR"
A possible solution to (1) would be:
documents.where('status', 'in', ['open', 'upcoming']);
See Firebase Guides: Query Operators | in and array-contains-any
suggest to give value for status as well.
ex.
{ name: "a", statusValue = 10, status = 'open' }
{ name: "b", statusValue = 20, status = 'upcoming'}
{ name: "c", statusValue = 30, status = 'close'}
you can query by ref.where('statusValue', '<=', 20) then both 'a' and 'b' will found.
this can save your query cost and performance.
btw, it is not fix all case.
I would have no "status" field, but status related fields, updating them to true or false based on request, like
{ name: "a", status_open: true, status_upcoming: false, status_closed: false}
However, check Firebase Cloud Functions. You could have a function listening status changes, updating status related properties like
{ name: "a", status: "open", status_open: true, status_upcoming: false, status_closed: false}
one or the other, your query could be just
...where('status_open','==',true)...
Hope it helps.
This doesn't solve all cases, but for "enum" fields, you can emulate an "OR" query by making a separate boolean field for each enum-value, then adding a where("enum_<value>", "==", false) for every value that isn't part of the "OR" clause you want.
For example, consider your first desired query:
Give me all documents where the field status is open OR upcoming
You can accomplish this by splitting the status: string field into multiple boolean fields, one for each enum-value:
status_open: bool
status_upcoming: bool
status_suspended: bool
status_closed: bool
To perform your "where status is open or upcoming" query, you then do this:
where("status_suspended", "==", false).where("status_closed", "==", false)
How does this work? Well, because it's an enum, you know one of the values must have true assigned. So if you can determine that all of the other values don't match for a given entry, then by deduction it must match one of the values you originally were looking for.
See also
in/not-in/array-contains-in: https://firebase.google.com/docs/firestore/query-data/queries#in_and_array-contains-any
!=: https://firebase.googleblog.com/2020/09/cloud-firestore-not-equal-queries.html
I don't like everyone saying it's not possible.
it is if you create another "hacky" field in the model to build a composite...
for instance, create an array for each document that has all logical or elements
then query for .where("field", arrayContains: [...]
you can bind two Observables using the rxjs merge operator.
Here you have an example.
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/merge';
...
getCombinatedStatus(): Observable<any> {
return Observable.merge(this.db.collection('foo', ref => ref.where('status','==','open')).valueChanges(),
this.db.collection('foo', ref => ref.where('status','==','upcoming')).valueChanges());
}
Then you can subscribe to the new Observable updates using the above method:
getCombinatedStatus.subscribe(results => console.log(results);
I hope this can help you, greetings from Chile!!
We have the same problem just now, luckily the only possible values for ours are A,B,C,D (4) so we have to query for things like A||B, A||C, A||B||C, D, etc
As of like a few months ago firebase supports a new query array-contains so what we do is make an array and we pre-process the OR values to the array
if (a) {
array addObject:#"a"
}
if (b) {
array addObject:#"b"
}
if (a||b) {
array addObject:#"a||b"
}
etc
And we do this for all 4! values or however many combos there are.
THEN we can simply check the query [document arrayContains:#"a||c"] or whatever type of condition we need.
So if something only qualified for conditional A of our 4 conditionals (A,B,C,D) then its array would contain the following literal strings: #["A", "A||B", "A||C", "A||D", "A||B||C", "A||B||D", "A||C||D", "A||B||C||D"]
Then for any of those OR combinations we can just search array-contains on whatever we may want (e.g. "A||C")
Note: This is only a reasonable approach if you have a few number of possible values to compare OR with.
More info on Array-contains here, since it's newish to firebase docs
If you have a limited number of fields, definitely create new fields with true and false like in the example above. However, if you don't know what the fields are until runtime, you have to just combine queries.
Here is a tags OR example...
// the ids of students in class
const students = [studentID1, studentID2,...];
// get all docs where student.studentID1 = true
const results = this.afs.collection('classes',
ref => ref.where(`students.${students[0]}`, '==', true)
).valueChanges({ idField: 'id' }).pipe(
switchMap((r: any) => {
// get all docs where student.studentID2...studentIDX = true
const docs = students.slice(1).map(
(student: any) => this.afs.collection('classes',
ref => ref.where(`students.${student}`, '==', true)
).valueChanges({ idField: 'id' })
);
return combineLatest(docs).pipe(
// combine results by reducing array
map((a: any[]) => {
const g: [] = a.reduce(
(acc: any[], cur: any) => acc.concat(cur)
).concat(r);
// filter out duplicates by 'id' field
return g.filter(
(b: any, n: number, a: any[]) => a.findIndex(
(v: any) => v.id === b.id) === n
);
}),
);
})
);
Unfortunately there is no other way to combine more than 10 items (use array-contains-any if < 10 items).
There is also no other way to avoid duplicate reads, as you don't know the ID fields that will be matched by the search. Luckily, Firebase has good caching.
For those of you that like promises...
const p = await results.pipe(take(1)).toPromise();
For more info on this, see this article I wrote.
J
OR isn't supported
But if you need that you can do It in your code
Ex : if i want query products where (Size Equal Xl OR XXL : AND Gender is Male)
productsCollectionRef
//1* first get query where can firestore handle it
.whereEqualTo("gender", "Male")
.addSnapshotListener((queryDocumentSnapshots, e) -> {
if (queryDocumentSnapshots == null)
return;
List<Product> productList = new ArrayList<>();
for (DocumentSnapshot snapshot : queryDocumentSnapshots.getDocuments()) {
Product product = snapshot.toObject(Product.class);
//2* then check your query OR Condition because firestore just support AND Condition
if (product.getSize().equals("XL") || product.getSize().equals("XXL"))
productList.add(product);
}
liveData.setValue(productList);
});
For Flutter dart language use this:
db.collection("projects").where("status", whereIn: ["public", "unlisted", "secret"]);
actually I found #Dan McGrath answer working here is a rewriting of his answer:
private void query() {
FirebaseFirestore db = FirebaseFirestore.getInstance();
db.collection("STATUS")
.whereIn("status", Arrays.asList("open", "upcoming")) // you can add up to 10 different values like : Arrays.asList("open", "upcoming", "Pending", "In Progress", ...)
.addSnapshotListener(new EventListener<QuerySnapshot>() {
#Override
public void onEvent(#Nullable QuerySnapshot queryDocumentSnapshots, #Nullable FirebaseFirestoreException e) {
for (DocumentSnapshot documentSnapshot : queryDocumentSnapshots) {
// I assume you have a model class called MyStatus
MyStatus status= documentSnapshot.toObject(MyStatus.class);
if (status!= null) {
//do somthing...!
}
}
}
});
}