I have implemented a basic auto-complete feature using jQuery autocomplete. I am querying DB every time which is making auto-complete thing quite slow. I am looking for ways to make it faster much like Quora.
Here is the code from front-end:
<script type="text/javascript">
var URL2 = '<?php e(SITE_URL); ?>fronts/searchKeywords';
jQuery(document).ready(function(){
var CityKeyword = jQuery('#CityKeyword');
CityKeyword.autocomplete({
minLength : 1,
source : URL2
});
});
</script>
Here is the code from server side:
function searchKeywords(){
if ($this->RequestHandler->isAjax() ) {
$this->loadModel('Expertise_area');
Configure::write ( 'debug',0);
$this->autoRender=false;
$expertise=$this->Expertise_area->find('all',array(
'conditions'=>array('Expertise_area.autocomplete_text LIKE'=>'%'.$_GET['term'].'%'),
'fields' => array('DISTINCT (Expertise_area.autocomplete_text) AS autocomplete_text'),
'limit'=>5
));
$i=0;
if(!empty($expertise)){
$len = strlen($_GET['term']);
foreach($expertise as $valueproductname){
$pos = stripos($valueproductname['Expertise_area']['autocomplete_text'],$_GET['term']);
$keyvalue = "";
if($pos == 0) {
$keyvalue= "<strong>".substr($valueproductname['Expertise_area']['autocomplete_text'],$pos,$len)."</strong>"
.substr($valueproductname['Expertise_area']['autocomplete_text'],$len);
}else {
$keyvalue= substr($valueproductname['Expertise_area']['autocomplete_text'],0,$pos)."<strong>"
.substr($valueproductname['Expertise_area']['autocomplete_text'],$pos,$len)."</strong>"
.substr($valueproductname['Expertise_area']['autocomplete_text'],$pos+$len);
}
$response[$i]['value']=$valueproductname['Expertise_area']['autocomplete_text'];
$response[$i]['label']="<span class=\"username\">".$keyvalue."</span>";
$i++;
}
echo json_encode($response);
}else{
}
}
}
I have researched a bit and so far following solutions are worth looking at:
Query data on page load and store it in COOKIE to be used in future.
Implement some caching mechanism (memcache??). But my website is on Cakephp which does it internal cahcing if I am right. So will it be worth to go in this direction.
Use some third party indexing mechanism like Solr, Lucene etc. Don't know much about this.
Implement a much complex "Prefix Search" myself
What is the right way to go about it? Please help me out here.
I've never tried this but will be doing it soon for a project I'm working on.
I always considered the possibility of during the initial page load recieveing some AJAX (or perhaps just including it in the page) the top 10 words for each alphabet letter.. e.g.
A - apples, anoraks, alaska, angela, aha, air, arrgh, any, alpha, america
B - butter, bob etc.....
This way when user presses A-Z you can instantly provide them with 10 of the most popular keywords without any further requests, as you already have them stored in an array in the JS.
I'm not sure of size/memory usage but this could be extended further to handle the first 2 letters, e.g. AA, AB, AC.....BA, BB, BC.... ZA, ZB, ZZ... of course many combinations such as words starting with ZZ won't have any data unless it's a music site and it's ZZ Top! This means it probably won't take up so much memory or bandwidth to send this data during initial page load. Only when the user types the 3rd letter do you need to do any further data lookups/transfers.
You auto-update this data every day, week or whatever depending on site usage and the most popular searches.
I am adding a solution to my question which I figured out after a lot of research.
Problem was:
I was using Ajax to fetch keywords from database every time a user changes text in search box
I was doing a wild card search to match search item within entire strings and not just starting of keywords for ex. "dev" would return "social development", "development" etc
Solution:
I have a fixed array of keywords (200) which is not going to increase exponentially in near future. So, instead of doing complex indexing I am currently sending all keywords in an array.
I am sending this data in an array on page load since it is small. If it becomes large, I will fetch it in background via some ajax in different indexed arrays.
I am using jQuery's Autocomplete widget to do rest of thing for me.
For highlighting search item, I am using a hack by working around __renderItem. (Copied from Stackoverflow. Thanks to that!!)
Code:
function monkeyPatchAutocomplete() { //Hack to color search item
jQuery.ui.autocomplete.prototype._renderItem = function( ul, item) {
var re = new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + this.term + ")(?![^<>]*>)(?![^&;]+;)", "gi");
var t = item.label.replace(re,"<span style='font-weight:bold;color:#434343;'>" +
"$&" +
"</span>");
return jQuery( "<li></li>" )
.data( "item.autocomplete", item )
.append( "<a>" + t + "</a>" )
.appendTo( ul );
};
}
function getKeywords(){
//Function that returns list of keywords. I am using an array since my data is small.
//This function can be modified to fetch data in whatever way one want.
//I intend to use indexed arrays in future if my data becomes large.
var allKeywords = <?php echo json_encode($allKeywords); ?>;
return allKeywords;
}
jQuery(document).ready(function(){
monkeyPatchAutocomplete();
var CityKeyword = jQuery('#CityKeyword');
CityKeyword.autocomplete({
minLength : 1,
source : getKeywords()
});
});
Related
I have an app that communicates with my API that runs php and mysql.
What I wanted to do was record changes that occur to entities in my table for each user. If a user makes a change to their data, I can see the change that occurred. This way if they ever have questions or accidentally delete something, I can go back and tell them what the entities looked like at various stages in the year.
I don't need to be crazy specific about the differences, all I would like to do is record inserts or updates (as it's represented in a JSON body).
Basically what I did for now was any time a POST/PUT occurs to my API for certain routes, I just take the JSON in the request body, and I save it to a record in the database as a transaction that took place for that user.
This was great early on, but after hundreds of thousands of records, the JSON body is large and is taking up a lot of room. My database table is 13GB. Queries take a while to run, too. I truncated it, but within 4 months it grew again to another 10GB. This problem will likely only get larger.
Is there an approach someone can recommend to record this? Can I maybe send the request body over to something on AWS or some other storage offline or another database somewhere else? Flat files perhaps or a non-relational database? It's not like I actually need the data in real time but if I ever wanted to get a history of someone I'd like to know I could.
I do take nightly backups of the DB, so an alternate approach was I was thinking of cutting out the transaction logs entirely, and instead just letting it continue to back up nightly. Sure, I won't be able to show a history of what dates entities were updated/added, but at least I could always reference a few backups to see what records were for a given user on a certain date after I do a restore.
Any ideas or suggestions? Thanks!
Instead of logging the entire JSON, you can just log the values that have changed and you also don't have to log your insert data as your database will always have the current record and logging the insert data is redundant.
You can implement a Diff function to compare difference in your existing JSON to the changed JSON.
To illustrate an example see the code below that borrows a JavaScript Diff function from this Answer.
// get the current value from your database
var oldvalues = {
"id": 50,
"name": "Old Name",
"description": "Description",
"tasks": [{
'foo': 'bar'
}]
};
var newvalues = {
"id": 50,
"name": "New name",
"description": "Description",
"tasks": [{
'foo': 'bar'
}]
};
var isEmptyObject = function(obj) {
var name;
for (name in obj) {
return false;
}
return true;
};
var diff = function(obj1, obj2) {
var result = {};
var change;
for (var key in obj1) {
if (typeof obj2[key] == 'object' && typeof obj1[key] == 'object') {
change = diff(obj1[key], obj2[key]);
if (isEmptyObject(change) === false) {
result[key] = change;
}
}
else if (obj2[key] != obj1[key]) {
result[key] = obj2[key];
}
}
return result;
};
var update = diff(oldvalues, newvalues);
//save this to your database
$('#diff').text(JSON.stringify(update));
textarea {
width: 400px;
height: 50px
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<textarea id="diff"></textarea>
As you can see only the only change that would be saved is {"name":"New name"} which will cut down on your data usage.
You would of course need to either port this PHP or look at some existing packages such as node-rus-diff
that might serve your needs.
As long as you are keeping a timestamp or a sequence number you can chain multiple transactions to rollback to any prior state. This is analogous to doing an incremental backup.
You could also run a maintenance task at set intervals if you would like to create checkpoints and compare a current state to a previous state. Perhaps once a month take a back up and record the differences between objects that have changed. This would be analogous to a differential backup.
Finally, you can take a full back up and clear out out the previous transactions, analogous to a full back up.
It is common practice for administrators to perform a combination of incremental, differential and full backups to balance storage costs and recovery needs. Using these approaches outline above you can implement the strategy that is right for you.
In trying to rejuvinate code I wrote mostly 14+ years ago. I've come to see that the lovely little setup I wrote then was... lacking in certain places, namely handling user inputs.
Lesson: Never underestimate users ability to inject trash, typos, and dupes past your validators.
The old way is reaching critical mass as there are 470 items in a SELECT dropdown now. I want to reinvent this part of the process so I don't have to worry about it hitting a breaking point.
So the idea is to build a fuzzy search method so that after the typist enters the search string, we check against five pieces of data, all of which reside in the same row.
I need to check the name submitted against the Stage Name, two also-known-as names, as well as their legal name and as a final check against a soundex() index based on their Stage Name (this catches a few spelling errors missed otherwise)
I've tried a complicated block of code to check these things (and it doesn't work, mostly because I think I coded the comparisons too strict) as part of a do/while loop.
In the below, var $Rin would contain the user supplied name.
$setr = mysql_query("SELECT ID,StageName,AKA1,AKA2,LegalName,SoundEx FROM performers");
IF ($R = mysql_fetch_array($setr)) {
do {
$RT = substr(trim($Rin), 5);
$RT1 = substr($R[1], 5);
$RT2 = substr($R[2], 5);
$RT3 = substr($R[3], 5);
$RT4 = substr($R[4], 5);
$RTx = soundex($RT);
IF ($RT == $RT1) {
$RHits[] = $R[0];
}
IF ($RT == $RT2) {
$RHits[] = $R[0];
}
IF ($RT == $RT3) {
$RHits[] = $R[0];
}
IF ($RT == $RT4) {
$RHits[] = $R[0];
}
IF ($RTx == $R[5]) {
$RHits[] = $R[0];
}
} while ($R = mysql_fetch_array($setr));
}
The idea being that I'll build an array of the ID#'s of the near hits, which I'll populate into a select dropdown that has only hopefully fewer hits that the whole table. Which means querying for a result set from the contents of that array, in order to display the Performer's name in the SELECT dropdown and pass the ID# as the value for those choices.
Thats when I hit the 'I need to use an array in my WHERE clause' problem, and after finding that answer, I am starting to suspect I'm out of luck due to Stipulation #2 below. So I started looking at alternate search methods and I'm not sure I've gotten anywhere but more confused.
So, is there a better way to scan a single table for six fields, checking five against user input and noting the sixth for display in a subset of the original table?
Thought process:
Against the whole table, per record, test $Rin against these tests in this order:
$Rin -> StageName
$Rin -> AKA1
$Rin -> AKA2
$Rin -> LegalName
soundex($Rin) -> SoundEx
where a hit on any of the five operations adds the ID# to a result array that is used to narrow the results from 470 performers down to a reasonable list to choose from.
Stipulations:
1) As written, I know this is vulnerable to an SQL injection attack.
2) Server runs PHP 4.4.9 and MySQL 4.0.27-Standard, I can't upgrade it. I've got to prove it works before money will be spent.
3) This is hobby-level stuff, not my day job.
4) Performers often use non-English names or elements in their names, and this has led to typos and duplication by the data entry typists.
I've found a lot of mysqli and PDO answers for this sort of thing, and I'm seeing a lot of things that only half make sense (like link #4 below). I'm working on getting up to speed on these things as I try and fix whats become broken.
Places already looked:
PHP mysql using an array in WHERE clause
PHP/MySQL small-scale fuzzy search
MySQL SubString Fuzzy Search
Sophisticated Name Lookup
I mentioned in the comments that a Javascript typeahead library might be a good choice for you. I've found Twitter's Typeahead library and Bloodhound engine to be pretty robust. Unfortunately, the documentation is a mixed bag: so long as what you need is very similar to their examples, you're golden, but certain details (explanations of the tokenizers, for example) are missing.
In one of the several questions re Typeahead here on Stack Overflow, #JensAKoch says:
To be honest, I think twitter gave up on typeahead.js. We look at 13000 stars, a full bugtracker with no maintainer and a broken software, last release 2015. I think that speaks for itself, or not? ... So, try one of the forks: github.com/corejavascript/typeahead.js
Frankly, in a brief check, the documentation at the fork looks a bit better, if nothing else. You may wish to check it out.
Server-side code:
All of the caveats of using an old version of PHP apply. I highly recommend retooling to use PDO with PHP 5, but this example uses PHP 4 as requested.
Completely untested PHP code. json_encode() would be better, but it doesn't appear until PHP 5. Your endpoint would be something like:
headers("Content-Type: application/json");
$results = mysql_query(
"SELECT ID,StageName,AKA1,AKA2,LegalName,SoundEx FROM performers"
);
$fields = array("ID","StageName","AKA1","AKA2","LegalName","SoundEx");
echo "[";
$first = true;
while ($row = mysql_fetch_array($results)) {
($first) ? $first = false : echo ',';
echo "\n\t,{";
foreach($fields as $f) {
echo "\n\t\t\"{$f}\": \"".$row[$f]."\"";
}
echo "\n\t}";
}
echo "]";
Client-side code:
This example uses a static JSON file as a stub for all of the results. If you anticipate your result set going over 1,000 entries, you should look into the remote option of Bloodhound. This would require you to write some custom PHP code to handle the query, but it would look largely similar to the end point that dumps all (or at least your most common) data.
var actors = new Bloodhound({
// Each row is an object, not a single string, so we have to modify the
// default datum tokenizer. Pass in the list of object fields to be
// searchable.
datumTokenizer: Bloodhound.tokenizers.obj.nonword(
'StageName','AKA1','AKA2','LegalName','SoundEx'
),
queryTokenizer: Bloodhound.tokenizers.whitespace,
// URL points to a json file that contains an array of actor JSON objects
// Visit the link to see details
prefetch: 'https://gist.githubusercontent.com/tag/81e4450de8eca805f436b72e6d7d1274/raw/792b3376f63f89d86e10e78d387109f0ad7903fd/dummy_actors.json'
});
// passing in `null` for the `options` arguments will result in the default
// options being used
$('#prefetch .typeahead').typeahead(
{
highlight: true
},
{
name: 'actors',
source: actors,
templates: {
empty: "<div class=\"empty-message\">No matches found.</div>",
// This is simply a function that accepts an object.
// You may wish to consider Handlebars instead.
suggestion: function(obj) {
return '<div class="actorItem">'
+ '<span class="itemStageName">'+obj.StageName+"</span>"
+ ', <em>legally</em> <span class="itemLegalName">'+obj.LegalName+"</span>"
}
//suggestion: Handlebars.compile('<div><strong>{{value}}</strong> – {{year}}</div>')
},
display: "LegalName" // name of object key to display when selected
// Instead of display, you can use the 'displayKey' option too:
// displayKey: function(actor) {
// return actor.LegalName;
// }
});
/* These class names can me specified in the Typeahead options hash. I use the defaults here. */
.tt-suggestion {
border: 1px dotted gray;
padding: 4px;
min-width: 100px;
}
.tt-cursor {
background-color: rgb(255,253,189);
}
/* These classes are used in the suggestion template */
.itemStageName {
font-size: 110%;
}
.itemLegalName {
font-size: 110%;
color: rgb(51,42,206);
}
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
<script src="https://twitter.github.io/typeahead.js/releases/latest/typeahead.bundle.js"></script>
<p>Type something here. A good search term might be 'C'.</p>
<div id="prefetch">
<input class="typeahead" type="text" placeholder="Name">
</div>
For ease, here is the Gist of the client-side code.
I've searched a lot for answers to this question, but I can't find them. Maybe because I'm not searching for the right keywords (because I don't know the right name) or there might be few resources on this subject.
I'd like to build a 'sorting system' in codeigniter for database results. Much like the magento layered navigation or the sorting that's on this site: http://www.wintersporters.nl/skigebieden. On there you can filter ski resorts by price, country, amount of slopes etcetera and the results are automatically updated via AJAX.
another example -> http://www.beslist.nl/products/cddvdrom/cddvdrom_5035278/ where you can sort by date, publisher, genre etcetera.
I'd like the system to be able to pass multiple variables like on wintersporters.nl where you can select multiple countries instead of only one and this way you're able to narrow down your search more and more by selecting multiple attributes until you've found the ski resort that fits you best. So every time you select an option it's preserved in the URL.
How do I achieve something like this? Via the $_GET[] array, with uri's or something totally different? Can anyone point me to a tutorial, snippet of code, explanation or anything related as I can't find out the right way to do this.
I'd be really grateful!
I was also thinking about the component or control which will give me the same functioalities for my web portal but then I developed by own by HTML and Ajax.
In Ajax, you can use
var checkboxValueArray = [];
var val = [];
$('input[name="subcatcheckbox"]:checked').each(function(i){
val[i] = $(this).val();
checkboxValueArray.push($('#subcat'+val[i]).text());
});
checkboxValueString = checkboxValueArray.toString();
checkboxValueString = checkboxValueString.replace(/\ /g, "-");
checkboxValueString = checkboxValueString.replace(/\_/g, "-");
checkboxValueString = checkboxValueString.replace(/\,/g, ":");
var checkboxValueNumberString = val.toString();
var checkboxValueNumberString = checkboxValueNumberString.replace(/\,/g, ":");
var pathName ='';
if(checkboxValueNumberString != ''){
pathName = "events_categories="+checkboxValueString+"&catid="+checkboxValueNumberString;
}
if(pathName != ''){
window.location.hash = "#!" + pathName;
}
if(pathName == ''){
window.location.hash = '';
}
getEventsParsedJSON(val);
By this way, I am creating an array on onClick event and then passing to the string to the URL through window.location.hash(ajax method). I developed this in a day. It's easy to develop.
I have a site that I want to display ads to 10% of my traffic. I am getting on average around 30,000 hits a day and want 10% of those users to see an ad from one of my advertisers.
What's the best way to go about implementing this?
I was thinking about counting the visitors in a database, and then every 10 people that visit 1 user gets an ad. Or is there a better way of going about it?
I'm no good with math, so I'm not sure what's the best approach.
Generate a random number between 1 and 10, and compare it to a fixed number, and your code will run on average 10% of the time:
if (rand(1,10) == 1) {
echo 'ad code';
}
You can make this per-user instead of per-pageview by storing whether that user was 'chosen' in their session.
session_start();
if (isset($_SESSION['show_me_ads']) || rand(1,10) == 1)
$_SESSION['show_me_ads'] = true;
echo 'ad code';
}
I use Google's DFP (Doubleclick for Publishers) to serve ads on my site. It's pretty robust. You have to have an AdSense account, but that's not very hard to obtain, it's just annoying to wait to be approved.
Once you have it set up and your ads loaded in, you can control how many people see your ad by percentage (such as the 10% you were talking about), total pageviews, etc.
Look into it: http://google.com/dfp
If you'd rather not use 3rd party software, I'd think the simplest way would be to randomize it so 1/10 visitors see your ad. The simple way would be:
if (rand(1,10) == 1) {
echo 'YOUR AD CODE HERE';
}
You said you're not good at math, and I understand that, I'm pretty horrible at it too, but basically, every time the page is loaded, it's "rolling" a 10-sided "dice". Every time it "rolls" a 1 (which would be 1 out of 10 times, or 10%), it'll display the ad. Otherwise, it'll be ignored.
The reason this is better than relying on counting the number of users (aside from simplicity) is that it will still roll 1 10% of the time whether you have 30,000 visitors or 3,000,000.
In its simplest form:
if (rand(1,10) == 1) {
echo $ad_content;
}
if(rand ( 1,10) == 1)
display_ads();
You can use
if(mt_rand(1,10)==10){
//show your code;
}
It will show ads to about 10% users
Why would you show ads to a few unlucky ones instead of randomly deciding per page impression (instead of per visitor)?
In php, you can just go ahead and write:
$adPercent = 10;
if (rand(0, 100) < $adPercent) {
echo '<div class="ads">Buy now!</div>';
}
if this was for google ads, then you would need to make the ad insertion optional (using the prob logic above), suggest something along the lines of Google Ads Async (asynchronous)
<script type="text/javascript"><!--
// dynamically Load Ads out-of-band
setTimeout((function ()
{
// placeholder for ads
var eleAds = document.createElement("ads");
// dynamic script element
var eleScript = document.createElement("script");
// remember the implementation of document.write function
w = document.write;
// override and replace with our version
document.write = (function(params)
{
// replace our placeholder with real ads
eleAds.innerHTML = params;
// put the old implementation back in place
document.write=w;
});
// setup the ads script element
eleScript.setAttribute("type", "text/javascript");
eleScript.setAttribute("src", "http://pagead2.googlesyndication.com/pagead/show_ads.js");
// add the two elements, causing the ads script to run
document.body.appendChild(eleAds);
document.body.appendChild(eleScript);
}), 1);
//-->
</script>
I don't know what is the exact name of this feature, but I need a feature where the user should be given a text box to search things. When they type in this text box, a matching list of values from database should be shown like a drop-down box.
The user can select one of the value and for will got submit.
I know this is already done on Internet anywhere, but what is this feature called?
Where can I find this feature to download and use for free?
Autocomplete from jQuery UI . SAYT opensource from Google.
http://jqueryui.com/demos/autocomplete/
http://code.google.com/p/search-as-you-type/
I found this code somewhere online, very very useful and very fast (tested for 30000 strings), have been using it ever since,
it uses regex
var rx = new RegExp('"([^"]*' + search + '[^"]*)"', 'gi');
var i = 0;
var num;
results = '';
while (result = rx.exec(string)) {
results += "\n" + result[1];
i += 1;
if (i >= 10) break;
}
I've put up the entire code on JSFiddle
NOTE: My code(The HTML part) is extremely messy, was trying too many things at once, just correct it to suit your requirements.
The easiest way to do this is to use the jQuery UI version here.