The project I am working on requires a multi-word search... just as an example search 'Sprint Apple iPhone'. If any one of these words matches a column in my database, the contents of the entire row must be displayed.
I believe I have got this secured from SQL injection now, but may be wrong. The main issue I am trying to solve now is that I can only search for one word at a time, for example, 'Sprint' or 'Apple' or 'iPhone' but if I put in the entire query no results are displayed.
Partial queries do work for example 'Sprin' or 'Ap' or 'iPho' ... not sure where I am going wrong. I have looked at several examples on StackOverflow that work for a single word query but can not find a multiple word solution.
Any help is appreciated!
<font size="+3" face="Verdana">xxxx</font>
<br><br>
<form name="form" action="xxxx.php" method="get">
<input type="text" name="q" size="60" />
<input type="submit" name="Submit" value="Search">
</form>
<table>
<?
$var = $_GET['q'];
try {
$conn = new PDO('mysql:host=localhost;dbname=xxxx', $username, $password);
$stmt = $conn->prepare("SELECT * FROM phones WHERE carrier LIKE :search OR
manufacturer LIKE :search OR model LIKE :search");
$stmt->execute(array('search' => "%$var%"));
$result = $stmt->fetchAll();
if ( count($result) ) {
foreach($result as $row) {
echo "<tr>";
echo "<td>" .$row['carrier']. "</td>";
echo "<td>" .$row['manufacturer']. "</td>";
echo "<td>" .$row['model']. "</td>";
echo "</tr>";
}
}
else {
echo "No results found.";
}
}
catch(PDOException $e) {
echo 'ERROR: ' . $e->getMessage();
}
?>
</table>
Consider how a multi-word keyword will look once the query is build:
SELECT ... WHERE somefield LIKE '%apple iphone%'
^^^^^^^^^^^^
You can't simply dump a bunch of words into a LIKE block and expect it to work. At best, you have to do some massaging:
... WHERE somefield LIKE '%apple%' AND somefield LIKE '%iphone%'
^^^^^ ^^^^^^
but this gets VERY ugly VERY VERY FAST for any "long" query string.
To make it easier, you simply switch to a fulltext index:
... WHERE somefield MATCH(somefield) AGAINST ('apple iphone')
I solved the problem myself, it wasn't actually a solution by using code, but by automatically combining Carrier, Manufacturer, and Model together in to 1 column in my database when they are originally inserted in to the database, then matching that 1 column against the search query provided by the user.
Related
I have been looking at the documentation but I can't seem to figure out how to correctly create a filter object for the buildfire.datastore.search query.
I have an address property on my object and I want to be able to type in a partial amount of the address and have it return. Below are the filter objects I have tried to pass into the search query:
search = {filter: {"$json.address": {"$regex": `/${this.state.search}/`}}};
search = {filter: {'$regex': {'$json.address': this.state.search}}};
Neither have worked. End goal is:
buildfire.datastore.search(search, 'location', cb);
EDIT:
I even tried to hardcode the regex in the docs:
"$or" : [
{"description": {"$regex":"/new /"}}
]
and it didn't work (I replaced 'new' with a string I knew would show).
I just inserted the following on the control side:
for(let i = 0 ; i < 50 ; i++) {
buildfire.datastore.insert({
name: "Address" + i
,address: i + " " + (i % 2 ? "Main ":"4th ") + (i % 3 ? "ave":"st" )
},function(){});
}
then did a search on the widget side like this:
<body>
<input type="text" id="criteria" /><button onclick="search()">Search</button>
<div id="results"></div>
<script>
function search(){
var cri = document.getElementById("criteria").value;
buildfire.datastore.search( {filter:{"$json.name": {"$regex": cri } } } , function(err,results){
document.getElementById("results").innerHTML = JSON.stringify(results);
});
}
</script>
</body>
Works fine. Given if you want the search to be more complex then you need to modify the regex statement for example case insensitivity.
Hope this helps
I have a simple search form that looks like this:
<form action="http://www.theurltosearch.com" method="post">
<input class="search-box" name="query" type="text" value="search all reports" />
<input type="submit" name="search" />
</form>
What I'm trying to accomplish
The search is pointing to whats really a filtering system using tags.
In order for the user to properly see the results of what they queried the query url has to look something like this http://www.theurltosearch.com/#/Kboxes the # and the K are important as its how the tagging system returns results where K stands for keyword.
For multi term queries the url has to look like this separated by a comma http://www.theurltosearch.com/#/Kboxes,Kmoving
A user should also get results when they enter a string query something like http://www.theurltosearch.com/#/K%22more%20stuff%22
Right now if someone used the search it would just take them to the url and not actually display any results matching their query.
How can I manipulate the url string to return the results how I've shown above?
My actual attempt
<script type="text/javascript">
window.onload = function(){
var form = document.getElementById("reports-search");
form.onsubmit = function(){
var searchText = document.getElementById("search-reports");
window.location = "http://www.urltosearch.com/#/K" + searchText.value;
return false;
};
};
</script>
<form id="reports-search" method="get">
<input class="search-box" id="search-reports" type="text" value="search all reports" /><!--search term was analysis-->
<input type="submit" name="search" />
</form>
returns
http://www.urltosearch.com/#/Kanalysis
and displays all results with the analysis tag
This attempt works succesfully if someone is searching a single keyword but not if the user is searching multiple or a string
How do I change the JS to achieve the other options?
Okay, here's a dog'n'bird implementation (ruff,ruff, cheap,cheap).
I've allowed the user to enter multiple terms, each separated with the pipe character | If you wish to allow the user to enter a url in essentially the same format as they'd receive by 'normal' keywords, you may wish to check the entered text first and if found, simply pass it straight through without changing it.
You'll notice, I've wrapped all search terms with " ", regardless of whether the term is multi-word or not. You could easily differentiate between a single-word term and a multi, by searching the string for a space character after the string.trim has removed leading/trailing spaces. I.e
if (trimmedTerm.indexOf(' ') == -1)
{
// single word search term
}
else
{
// multi-word search term here
}
Anyway, here's a working demo, hope it gives insight.
function byId(id){return document.getElementById(id)}
// useful for HtmlCollection, NodeList, String types
function forEach(array, callback, scope){for (var i=0,n=array.length; i<n; i++)callback.call(scope, array[i], i, array);} // passes back stuff we need
window.addEventListener('load', onDocLoaded, false);
function onDocLoaded(evt)
{
byId('goBtn').addEventListener('click', onGoBtnClicked);
}
function onGoBtnClicked(evt)
{
// get the user input
var inputString = byId('userInput').value;
// split it into an array of terms, based on the | char
var searchTerms = inputString.split('|');
// init the result
var result ='';
// for each element in the array of search terms, call the function to trim wrap with "" and encode
forEach(searchTerms, addCurTermToResult);
// update the output display
byId('output').textContent = 'http://www.theurltosearch.com/#/' + result;
function addCurTermToResult(curTerm, index)
{
if (index != 0) // put a comma before all terms except the first one
result += ',';
var trimmedTerm = curTerm.trim(); // remove leading/trailing spaces
result += 'K' + encodeURI('"' + trimmedTerm + '"' ); // wrap with "" then URI encode it, suitable for use as a URL
}
}
.panel
{
border: solid 1px black;
border-radius: 8px;
padding: 8px;
background-color: #eef;
display:inline-block;
}
.panel textarea
{
width: 500px;
height: 200px;
}
<div class='panel'>
<textarea type='text' id='userInput' placeholder='Enter tags or a url. tags should be seperated with the | character'></textarea>
<div style='text-align: center'><button id='goBtn'>Submit</button></div>
<hr>
<label>URL: <span id='output'></span></label>
</div>
I have a search box on my downloads site, where you're meant to type in the name of an application and if I have it on my downloads site, it will come up under the search box.
However, my issue is that when I search anything, it doesn't come up with any results - clarification on what I've done wrong would be appreciated.
<form name="smartForm" method="POST">
<input class="field" type="text" name="app" placeholder="Name of Application">
</select><br><br>
<input class="form_btn" type="submit" value="Search">
</form><br>
<!-- LET THE FORM PHP BEGIN! -->
<?php // gets elements by tagnames
$app = $_POST['app'];
$space = "<br />";
// following code will echo search results
echo '<strong><u>Form Results</u></strong><br />';
echo 'Application Searched: '.$_POST['app'].$space;
// finish PHP
?>
I'm using the following code to populate a WordPress dropdown menu with all the unique values from a custom field:
<form name="search" action="" method="get">
<select name="stateprov">
<option>Select...</option>
<?php
$metakey = 'state_prov';
statesProvs = $wpdb->get_col($wpdb->prepare("SELECT DISTINCT meta_value FROM $wpdb->postmeta WHERE meta_key = %s ORDER BY meta_value ASC", $metakey) );
if ($statesProvs) {
foreach ($statesProvs as $stateprov) {
echo "<option value=\"" . $stateprov . "\">" . $stateprov . "</option>";
}
}
?>
</select>
<input type="submit" value="search" />
</form>
However, it takes nothing from the DB so the popup list is empty.
Trying a different query like
$statesProvs = $wpdb->get_col( "SELECT ID FROM $wpdb->posts WHERE post_author = 2" );
Works as expected. I get a popup with a bunch of Post ID's in it. But the query that's supposed to work on my custom metadata just brings up an empty menu (and print_r reveals an empty array).
The data is definitely in the DB... what am I doing wrong?
It may also be significant that I'm using a custom metabox PHP class that writes all the custom-created field keys and values into the value of the _custom_meta metakey. If I've put that correctly. Thus:
a:61:{s:10:"state_prov";s:2:"CA";s:13:"vertical_drop";s:13:"3100ft / 945m";s:14:"base_elevation";s:14:"7953ft / 2424m";s:16:"summit_elevation";s:15:"11053ft / 3369m";s:12:"skiable_area";s:10:"3500 acres";s:16:"average_snowfall";s:14:"400in / 1016cm";s:13:
Etc. Is this way of storing the custom metadata preventing wpdb from accessing it properly?
Thanks!
you must use global $wpdb before you start the wpquery, refer codex for more details
<form name="search" action="" method="get">
<select name="stateprov">
<option>Select...</option>
<?php
global $wpdb;
$metakey = 'state_prov';
$wpdb->get_col($wpdb->prepare("SELECT DISTINCT meta_value FROM $wpdb->postmeta WHERE meta_key = %s ORDER BY meta_value ASC", $metakey) );
if ($statesProvs) {
foreach ($statesProvs as $stateprov) {
echo "<option value=\"" . $stateprov . "\">" . $stateprov . "</option>";
}
}
?>
</select>
<input type="submit" value="search" />
</form>
Etc. Is this way of storing the custom metadata preventing wpdb from
accessing it properly?
Yes.
In general, Wordpress isn't capable of searching or sorting through serialized PHP arrays.
The most viable solution here is to store this data point ('state_prov') in its own custom field. This will allow you to naturally search and sort using $wpdb. It also seems like you want this custom field to be private/hidden, in which case you'll want to prefix it with an underscore: '_state_prov'.
Another option, assuming the above isn't viable for your needs, is to use MySQL String Functions; but this is far from ideal or optimized (especially if the database grows significantly.) Rough, untested example below.
// Assuming all state_prov are exactly 2 characters in length
$identifier = 's:10:"state_prov";s:2:"';
$identifier_length = strlen($identifier);
$wpdb->get_col($wpdb->prepare("SELECT DISTINCT(SUBSTR(`meta_value`, INSTR(`meta_value`, '$identifier')+$identifier_length, 2)) as `state_prov` FROM $wpdb->postmeta WHERE `meta_key`=%s ORDER BY `state_prov` ASC", $metakey) );
I come back with a symfony2 problem I have.
I'm trying to make a really simple "search form" to display some posts of a blog. To not overkill it, I've decided to create the form directly in the twig like this:
<form class="form-search" method="post" action="{{ url('search_route') }}">
<input type="text" placeholder="Search" class="input-medium search-query" name="search">
<button type="submit"><img src="/img/search.png" alt="search" /></button>
</form>
In my controller I'm trying to find a way of how to pass the value of the input in my query. Here is the code of the searchAction():
use Symfony\Component\HttpFoundation\Request;
/..
public function searchAction(Request $request)
{
$data = $request->request->all();
$dql = "SELECT a FROM PfBlogBundle:Article a WHERE a.title LIKE '{$data['search']}' ORDER by a.id DESC";
$query = $em->createQuery($dql);
$paginator = $this->get('knp_paginator');
$pagination = $paginator->paginate(
$query,
$this->get('request')->query->get('page', 1)/*page number*/,
4/*limit per page*/
);
return $this->render('PfBlogBundle:Default:blog.html.twig', array('pagination'=>$pagination));
}
the fact is that if I print_r($data), I have the value sent through the input.. My problem is really to pass it in the query I think.. I'm developing locally and get a server error in the browser when I hit submit :/
Any idea?