I need to do a search on a text field (mongodb). The Search criteria / filter on the collection can be any substring of the sting in the field. I save the filter (input) in a session variable. The filter seems to work the first time after the Sessionvariable is null.
After the first search I get no result when I enter a new (extisting!) value. In case I clear the filter, hit enter and than reenter the filter I get the result.
I assume that my code is suboptimal. Can someone please review and maybe make a suggestion?
Here is what I have:
html:
<input class="medinput" maxlength="80" type="text" name="nameSearchBar" id="nameSearchBar" value="{{searchText}}">
javascript:
Session.setDefault('searchText', null);
Template.questions.helpers({
questions:function(){
if ((Session.get('searchText') === null) || (Session.get('searchText') === '')) {
Session.get('searchText',null);
return Questions.find({archived:{$ne:true}});
} else {
var searchText = Session.get('searchText');
Session.get('searchText',null);
return Questions.find( { $and: [ { archived: { $ne: true } }, {note:{$regex: (".*"+searchText+".*") } } ] } )
}
},
searchText:function(){
return Session.get('searchText');
}
})
Template.questions.events({
"change #nameSearchBar":function(event){;
searchText = event.target.value;
Session.set('searchText', searchText);
}
})
Questions:
why do I need to first "enter" an empty string to get a valid result
is this a good procedure? Doesn't hitting enter works against the reactive
approach? I think after enter the complete page is resent ??
Would it be better to check "keyup" for ASCII 27 and 13?
it seems to me that the regex works, are there any concerns? I like to find any substring of the typed in filter, also when the filter has a whitespace.
this is a full text search do I need to set something up on the mongodb side?
Open Question: are the already prebuild implementiations - I do not need to reinvent the wheel...
Here's my interpretation, or how I would possibly have built this:
Template.questions.helpers({
questions:function(){
var searchText = Session.get('search-text');
if(searchText && searchText != '') {
return Questions.find({$and: [{archived: {$ne: true}}, {note:{$regex: (".*"+searchText+".*")}}]});
} else {
return Questions.find({archived: {$ne: true}});
}
}
});
Template.questions.events({
"keyup #nameSearchBar": _.throttle(function(event){
searchText = event.target.value;
Session.set('searchText', searchText);
}), 500)
});
To me it doesn't seem necessary to set a default, or to call Session.get('search-text', null). Also you'll see how my code is a bit more concise. keyup is probably more effective as an event here too.
N.B. The _.throttle is an underscore.js function which will limit changing the Session variable to only every 500ms rather than constantly. This helps save unnecessary server load.
Two pre-built packages that are popular amongst Meteor developers are worth looking into: Easy Search or Search Source
I copied a generally working example for a search on a single text field.
#Ian: Thanks for the suggestion! I took the toggle part, I found that you can not leave the session variable as is, even it gets over written, it must evaluated before. So it has to be set back with NULL.
Session.setDefault('searchText', null);
Template.questions.helpers({
questions:function(){
if ((Session.get('searchText') === null) || (Session.get('searchText') === '')) {
return Questions.find({archived:{$ne:true}});
} else {
var searchText = Session.get('searchText');
return Questions.find( { $and: [ { archived: { $ne: true } }, {note:{$regex: (".*"+searchText+".*") } } ] } )
}
},
searchText:function(){
return Session.get('searchText');
}
})
Template.questions.events({
'keyup #nameSearchBar': _.throttle(function(event){;
Session.set('searchText',null);
searchText = event.target.value;
Session.set('searchText', searchText);
},500)
})
Related
Trying to do Cypress Testing with my React app.
I'm retrieving an object with an attribute expirationDate from the backend. It's an integer with format YYYYMMDD. In my corresponding frontend in the <input> component, it's rendered as an YYYY-MM-DD string.
However the object may optionally not have an expiration date at all, which is instead represented as the attribute being -1 or -2. This is presented as an empty string '' in the <input>.
I thus need to conditionally check the value. How do I go about doing this with Cypress?
Closest I have right now is
cy.get('#input-expiration-date').should('have.value', expirationDate || '')
But this is not really an accurate test of the above conditions.
Conditional testing can be done as follows, but this is bad practice.
cy.get('#input-expiration-date')
.invoke('val')
.then(val => {
if (val === expirationDate) { // fails because page is still fetching
...
}
})
The test runs faster than the value can be fetched from the server.
You can avoid the issue by waiting for the object fetch with cy.intercept() and do the conditional check then.
let expirationDate = 'YYYYMMDD'
cy.intercept(url).as('object')
cy.visit(...)
cy.wait('#object').then(object => {
if (object.expirationDate) {
cy.get('#input-expiration-date')
.should('have.value', expirationDate)
}
})
you can do:
cy.get('#input-expiration-date').then(element => {
//and here you can have conditions and access the DOM element
})
There a Chai method oneOf() you can use.
Cypress uses Chai internally, so the expression works inside .should().
cy.get('#input-expiration-date')
.invoke('val')
.should('have.oneOf', [expirationDate, ''])
Using Chai directly
cy.get('#input-expiration-date').then($input => {
expect($input.val()).to.have.oneOf([expirationDate, ''])
})
Or if you want to check and further do some actions based on the value, you can do something like this:
cy.get('#input-expiration-date')
.invoke('val')
.then((val) => {
if (val == expirationDate) {
//Do something
} else if (val == '') {
// Do something
} else {
//Do something
}
})
I'm trying to check if there is an option in my select using the code below, but it keeps failing, can somenone give some help?
My Select have about 70 names and I'm trying to loop all them looking for the specific name.
cy.get('[id="names"] option').each(($ele) => {
expect($ele).to.have.text('Have This Name')
})
Thanks in advance,
I would not use .each(), only one will pass but any other will fail.
Use .contains() if your text is specific enough (not in multiple options)
cy.contains('[id="names"] option', 'Have This Name') // fails only if
// no option has the text
If you have to match exactly, filter the options
cy.get('[id="names"] option')
.filter((idx, el) => el.innerText === 'Have This Name') // fails if filter
// returns 0 items
If you want .each() for another reason, this will do
let found;
cy.get('[id="names"] option')
.each(($option) => {
if ($option.text() === 'Have This Name') {
found = $option
return false // return now, have found it
}
})
.then(() => { // after loop exit
expect(found).to.have.text('Have This Name')
})
You can handle the scenario like this-
cy.get('[id="names"] option').then(($ele) =>
{
if ($ele.text().includes('Have This Name'))
{
// do your statements
}
else
{
// do your statements
}
})
I have an array of objects that represent form fields. I want to make sure that none of the elements in the array have their initial values. As in all fields need to be updated. The initial value is below. I'd like a simple function that checks each element and ensures none of these properties have the default fields.
[
{
key: 0,
name: '',
typeLabel: '',
typeValue: 0,
value: '',
}
...
]
Looking for help on improving this
collection.every((obj) => {
if (obj.key === 0) return false;
if (obj.name === '') return false;
if (obj.typeLabel === '') return false;
if (obj.typeValue === 0) return false;
if (obj.value === '') return false;
return true;
});
EDIT: Actually just thought of this
collection.every((obj) => {
const values = Object.values(obj);
return values.includes(0, '');
});
To be honest I'm not sure how much more better you can make it, it looks pretty decent to me! Any changes I'd make would be subjective changes that don't actually matter. Curious what other people will answer with though, maybe I'm missing something :)
I mean you could do this and it's arguably less if/elses and uses a named function to explain what it is doing which is maybe nicer? But meh I don't think this matters all that much:
function inputValueHasBeenSet(inputObject) {
return !!inputObject.key &&
!!inputObject.name &&
!!inputObject.typeLabel &&
!!inputObject.typeValue &&
!!inputObject.value;
}
collection.every(inputValueHasBeenSet);
I am creating a login system and i want to implement a username system automatically after registration with the user first-name and last-name. Everything is working fine but in the case if the registered user with the same first-name and last-name is already in the system i want to concatenate a incrmental number to it.
Example if : firstname:Badmus Lastname:Kabiru is in the system as badmus.kabiru and the newly registered user is also named so the new user username will be badmus.kabiru.1 the next will be badmus.kabiru.2.
My code sample are.
assignUserTrendname: function(req_username, callback){
let userNewname = fetchUserName(req_username);
let inc = 1, newlyAssignUsername;
userNewname.then((usernames) => {
console.log(req_username+" ...................... "+usernames); //The data from the database is loging out
if (usernames.atusername == null || usernames.atusername == undefined) {
newlyAssignUsername = req_username;
console.log("Assign automaticaly "+ newlyAssignUsername);
} else {
newlyAssignUsername = req_username;
console.log(`Username Result is DB: ${usernames.atusername} Req: ${newlyAssignUsername} Search ${inc}`);
if(usernames.atusername.toString() == newlyAssignUsername.toString()){
console.log("User name exit and inc is "+ inc);
inc++;
newlyAssignUsername = `${req_username}.${inc}`;
console.log("New search..."+ newlyAssignUsername);
fetchusernames(newlyAssignUsername); // These is not fetching from the database
}
newlyAssignUsername = `${req_username}.${inc}`;
}
console.log("Assigned is ......"+ newTrendname);
callback(null, newTrendname);
})
.catch((err)=> {console.log(err); throw err;});
}
function fetchUserName(trendname){
return Trender.getUserByTrendname(trendname);
}
If i am taking the wrong route please let me know.
Thanks.
In the scenario that your.username already exists you can search your Users with a regex pattern: ((?:your\.username)\d+$). This will get all records that match: your.username{num} where {num} is any number. If your username's are formatted as your.username.123 the pattern would be: ((?:your\.username\.)\d+$).
Assuming this returns an array, existing_users, you can count the records, since you'll always be incrementing by one, which will give you your next incremented number. Pseudo code:
let inc = existing_users.length + 1;
However, in the scenario that you delete a user your count is going to be off. You would need to loop over your existing_users and extract the number at the end and only keep the largest number.
let largest_num = 0;
existing_users.forEach(user => {
let num = user.match(/\d+$/);
if ( num != null && parseInt(num[0]) > largest_num ) {
largest_num = num[0];
}
});
Then you could do the same as above: let inc = largest_num + 1 and add that to your your.username string.
I do not know what library/framework you're using to search your MongoDB so I cannot write a query and function snippet.
We cannot write your code for you. A general regex has already been given that could be a potential way to solve your problem. If you go that route you can make use of the $regex operator. If you were to store the increment of the username as a separate field you could also sort by that to get the max value as well.
Here is an example of that:
db.users.find({
username: {
$regex: < Your Regex Here >
}
}).sort({
usernameIncrement: -1
}).limit(1);
Please see:
http://mongoosejs.com/docs/queries.html
https://docs.mongodb.com/manual/reference/operator/query/regex/
https://docs.mongodb.com/manual/reference/method/cursor.sort/
I am making a bookmark function that looks as follows:
$scope.bookmarkPost = function(bookmark_post){
if(window.localStorage.bookmarks == null) {
user_bookmarks = [];
} else {
user_bookmarks = JSON.parse(window.localStorage.bookmarks)
}
var existing_post = find(user_bookmarks, function(post){ return post.title == bookmark_post.title; });
if(!existing_post){
user_bookmarks.push({
id: bookmark_post.pagid,
title : bookmark_post.title,
date: bookmark_post.created,
room: bookmark_post.room
});
}
console.log(JSON.stringify(user_bookmarks));
window.localStorage.bookmarks = JSON.stringify(user_bookmarks);
};
This should work and indeed adds the post to a my array of objects if it allready exists, and then puts it in localstorage. As you can see I'm trying to check if there's allready an entry with the same title like this:
var existing_post = find(user_bookmarks, function(post){ return post.title == bookmark_post.title; });
To be 100% honest I'm not even 100% sure what this does, but I couldn't find anything else on my particular subject. This check doesn't work and thus there are duplicate entries. Does anyone know how it can be fixed?
You probably meant to use the Array.find() method:
var existing_post = user_bookmarks.find(function(post){
return post.title === bookmark_post.title; //use === for strict comparison
}); //will return only first element that matches the callback criteria
Quick note: Array.find() is not supported by IE