PHP Yii MongoDB in string query - php

I need to find all schools with contains a certain emailaddress in a string
At this moment, I'm sending a query to my database to obtain a list of Schools like this
$aSchools = School::model()->findAllByAttributes(array(
'finished' => School::SCHOOL_CREATED,
));
After that, I itterate over all the schools to check if they containt the mailAddress like this:
$aFoundSChools = array();
foreach($aSchools as $oSchool)
{
if (strpos($oSchool->mailAddress, Yii::app()->user->mailAddress))
{
$aFoundSChools [] = $oSchool;
}
}
But I'm guessing this could be cleaner, right? Can i do that in a single function, like a 'LIKE' query in sql?

MongoDB supports "like" queries in the form of regular expressions. Be warned though that this can not use an index. From plain PHP, you'd construct it like this:
$query = array(
'finished' => School::SCHOOL_CREATED,
'mailAddress' => new MongoRegexp( '/' . addslashes( Yii::app()->user->mailAddress ) . '/' ),
);
You'll have to use the addslashes if you think your mail address might contain a /.
I don't quite know Yii, but I expect you can use $query like this, just replacing your already existing test for just 'finished':
$aSchools = School::model()->findAllByAttributes( $query );

Related

Like query in mongodb to match any wording out of the name in PHP

I am working on a app where i am implementing the web api in flight framework. I am using mongodb database & want to do "Like" query in the database to retrieve records where the restaurant name matching the given words anywhere in its name.
I am doing this with following way :
$mongoDb = MongoDbConnector::getCollection('restaurants');
$regex = new MongoRegex('/\b' . $restaurantName . '\b/i');
$restaurantListDataCount = $mongoDb->find(array('city' => $city, 'name' => $regex))->count();
thing is i am getting count as 0 here even though more than 100 records having the particular string i am searching for exists in its name.
How the regex be in a way modified to make it work?
Just Change like that
$regex = new MongoRegex('/.*' . $restaurantName . '.*/i');

Sphinx - How to escape user input for SphinxQL?

I have website where users can search posts by entering keywords,
I am using Sphinx search for full text search, everyhting is working as expected.
But when i enter/input some special charaters in search query the search dosnt complete and throws error.
e.g.
keyword i search for :
hello)
my query for sphinxql :
SELECT id FROM index1 WHERE MATCH('hello)')
error i get :
index index1: syntax error, unexpected ')' near ')'
my php code looks like this
<?php
$sphinxql = mysqli_connect($sphinxql_host.':'.$sphinxql_port,'','') or die('ERROR');
$q = urldecode($_GET['q']);
$sphinxql_query = "SELECT id FROM $sphinx_index WHERE MATCH('".$q."') ";
?>
How can i escape user input and make sure the query wont brake and return the result set ?
You should use SQL escaping, to avoid SQL injection.
http://php.net/manual/en/mysqli.real-escape-string.php
$sphinxql_query = ".... MATCH('".mysqli_real_escape_string($sphinxql,$q)."') ";
... BUT you may want to ALSO, escape extended syntax.
See the FIRST THREE POSTS (after that it delves into misunderstanding) in this thread in the sphinx forum
http://sphinxsearch.com/forum/view.html?id=13619
For a simple solution.
The function in that thread, can be used to make your query work. It will escape the ) and stop it being taken as a operator.
BUT, it also means you WONT be able to use any search operators - because it blindly escapes them ALL. (which is the confusion later in the thread)
If you want to be able to use some or all operators, need to use more advanced escaping. (which I dont have a good solution for)
Edit: actully lets go the whole hog...
<?php
//Escapes all the Extended syntax, so can accept anything the user throws at us.
function EscapeString ( $string ) {
$from = array ( '\\', '(',')','|','-','!','#','~','"','&', '/', '^', '$', '=' );
$to = array ( '\\\\', '\(','\)','\|','\-','\!','\#','\~','\"', '\&', '\/', '\^', '\$', '\=' );
return str_replace ( $from, $to, $string );
}
if ($allow_full_extended_syntax) {
$q = $_GET['q'];
// the user is responsible for providing valid query.
} elseif ($allow_partical_extended_syntax) {
$q = InteligentEscape($_GET['q']);
//I don't have this function, it would need to be created.
} else {
$q = EscapeString($_GET['q']);
// escapes ALL extended syntax. NO operators allowed
}
$sphinxql_query = ".... MATCH('".mysqli_real_escape_string($sphinxql,$q)."') ";
Then it sounds like you want both $allow_full_extended_syntax and $allow_partical_extended_syntax set to false. Which means no operators will work, because they will be fully escaped.
The EscapeString function needs to escape the < character as well. Also see escapeString function in PECL shpinx for reference.

Atomic string replacement in PHP

In our web application, we have a class which emulates prepared statements for correct escaping of SQL parameters.
Now, I now it would be much better to just use PDO, but the app is very old and the refactoring could be quite long, so for the time being I just wanted to fix a bug I found.
Consider this piece of code which uses our classes:
$s = Q()->statement("SELECT * FROM DUAL WHERE 1 = :a AND 2 = :b AND 3 = :c");
$s->bindValue('c', ':b');
$s->bindValue('b', ':a');
$s->bindValue('a', ':c');
var_dump($s->prepared);
the first line creates the statement, then some values are bound, then I dump the prepared statement.
The result of this is the following:
SELECT * FROM DUAL WHERE 1 = ':c' AND 2 = '':c'' AND 3 = ''':c'''
and this happens because the parameters are substituted one at a time from the last to the first.
I also tried doing the replacement in a single function call, using str_replace() with array parameters, but to no avail.
So I would like to know if there is a way to make this operation somehow "atomic" so that if a placeholder value is another valid placeholder it does not get replaced.
EDIT:
Here is the method of my class which does the replacement:
protected function prepare() {
if (!$this->db) {
trigger_error (__METHOD__ . ': no Connection available to properly quote the value', E_USER_WARNING);
return false;
}
$this->prepared = str_replace(
array_map(array($this, 'getPlaceholderName'), array_keys($this->params)),
array_map(array($this->db, 'quote'), array_values($this->params)),
$this->original
);
return true;
}
You probably want a single call to good old strtr() whith the array signature:
string strtr ( string $str , array $replace_pairs )
Of course, plain string replacement is just a hack and can never replace a proper SQL parser, but I guess you already know that.
This is the code you want to use:
$queryString = strtr($queryString, array(":a" => "b",":b"=>"c", ":c" => "a"));

Link not concatenating the way I imagine it to be

I created this variable to store this string:
$extension = '/index.php?Ah83kL80='.$id;
And I'm trying to add the $extension to this link:
<a href="'.Yii::app()->createUrl('image/index',array('album'=>$album->content)).'">
So far, I tried doing this:
<a href="'.Yii::app()->createUrl('image/index',array('album'=>$album->content, 'index' => $extension)).'">
But it put some sort of other characters that I never intended to include.
Output:
/index/%2Findex.php%3FAh83kL80%3D
According to the documentation I found, the second param is an array of URL parameters. That means that you don't want a string like '?Ah83kL80='.$id but want to pass the Ah83kL80 key and $id value separately, as you're already doing with album and $album->content.
I'm not familiar with Yii but try building your link like this... I'm going to format this differently than you have so I can indent and make it easier to read.
$link = Yii::app()->createUrl(
'image/index',
array(
'album'=>$album->content,
'Ah83kL80' => $id,
),
);

Is this the right way to use Sphinx from PHP?

I am just starting with Sphinx. So far I got it installed successfully, got a table called profiles on my MySQL database indexed and am able to get the correct results back using the PHP API. I am using CodeIgniter so I wrapped the default PHP API as a CodeIgniter library.
Anyway this is how my code looks like:
$query = $_GET['q'];
$this->load->library('sphinxclient');
$this->sphinxclient->setMatchMode(SPH_MATCH_ANY);
$result = $this->sphinxclient->query($query);
$to_fetch = array();
foreach($result['matches'] as $key => $match) {
array_push($to_fetch, $key);
}
The array $to_fetch contains the ids of the matched table rows. Now I can use a typical MySQL query to get all the relevant users to display on the search page like so:
$query = 'SELECT * FROM profiles WHERE id IN('. join(',', $to_fetch) . ')';
My question are:
is this the right way to go about it? or is there a default "Sphinx way of doing it" that would be better for performance .
secondly, all I get back at the moment is the id of the matched table rows. I also want the part of the text in the column that matched. For example if a someone searches for the keyword dog and a user on the profiles table had in their about column the following text:
I like dogs. I also like ice cream.
I would like Sphinx to return:
I like <strong>dogs</strong>. I also like ice cream.
How can I do that? I tried to play around with the buildExcerpts() function but can't get it to work.
EDIT
This is how I am getting excerpts now:
// get matched user ids
$to_fetch = array();
foreach($result['matches'] as $key => $match) {
array_push($to_fetch, $key);
}
// get user details of matched ids
$members = $this->search_m->get_users_by_id($to_fetch);
// build excerpts
$excerpts = array();
foreach($members as $member) {
$fields = array(
$member['about'],
$member['likes'],
$member['dislikes'],
$member['occupation']
);
$options = array(
'before_match' => '<strong class="match">',
'after_match' => '</strong>',
'chunk_separator' => ' ... ',
'limit' => 60,
'around' => 3,
);
$excerpt_result = $this->sphinxclient->BuildExcerpts($fields, 'profiles', $query, $options);
$excerpts[$member['user_id']] = $excerpt_result;
}
$excerpts_to_return = array();
foreach($excerpts as $key => $excerpt) {
foreach($excerpt as $v) {
if(strpos($v, '<strong class="match">') !== false) {
$excerpts_to_return[$key] = $v;
}
}
}
As you can see I am searching each query across 4 different mysql columns:
about
likes
dislikes
occupation
Because of this I don't know which of the 4 columns contains the matched keyword. It could be any of them or even more than one. So I have no choice but to run the contents of all 4 columns through the BuildExcerpts() function.
Even then I don't know which one the BuildExcerpts() returned with the <strong class="match"> tags. So I run a stpos check on all values returned by BuildExcerpts() to finally get the proper excerpt and map it to the user whose profile it belongs to.
Do you see a better way than this given my situation where I need to match against the contents of 4 different columns?
Yes that looks good way. One thing to remember the rows coming back from Mysql probably won't be in the order from sphinx.
See the FAQ on sphinx site for how to use FIELD() but personally I like to put the rows from sphinx into associative array, then just loop though the sphinx I'd list and get the row from the array. Avoids a sorting phase altogether at the expense of memory!
As for highlighting, yes do persevere with buildExcerpts - that's is the way to do it.
edit to add, this demo
http://nearby.org.uk/sphinx/search-example5-withcomments.phps
demonstrates both getting rows from mysql and "sorting" in the app. And buildExcerpts.

Categories