MongoDB find query with $and $or and $nearSphere in php - php

After quite some trying out and web research I go crazy with this query. I want to build a query for 'Clubs' around a geo point (distance max 500 meters) in php on MongoDB.
But when I run query it ignores the distance limit and shows all clubs in database BUT sorted by distance.
Here is my dataset (2dsphere index geoLoc):
{"_id":ObjectId("547c649e30afe32c23000048"),"name":"Club Ritzz","category":"Club","category_list":[{"id":"191478144212980","name":"Night Club"}],"location":{"city":"Mannheim"},"geoLoc":{"type":"Point","coordinates":[8.473665839156,49.484065272756]}}
{"_id":ObjectId("547c649f30afe32c2300004a"),"name":"Das Zimmer Mannheim","category":"Club","category_list":[{"id":"191478144212980","name":"Night Club"}],"geoLoc":{"type":"Point","coordinates":[8.4709362941178,49.487260552592]}}
{"_id":ObjectId("547c64ab30afe32c23000063"),"name":"Nationaltheater Mannheim","category":"Arts/entertainment/nightlife","category_list":[{"id":"173883042668223","name":"Theatre"}],"geoLoc":{"type":"Point","coordinates":[8.4776534992592,49.48782606969]}}
{"_id":ObjectId("547c64a130afe32c2300004f"),"name":"SOHO Bar Club Lounge","category":"Club","category_list":[{"id":"191478144212980","name":"Night Club"},{"id":"164049010316507","name":"Gastropub"}],"geoLoc":{"type":"Point","coordinates":[8.4630844501277,49.49385193591]}}
{"_id":ObjectId("547c64a730afe32c2300005a"),"name":"Loft Club","category":"Club","category_list":[{"id":"191478144212980","name":"Night Club"},{"id":"176139629103647","name":"Dance Club"}],"geoLoc":{"type":"Point","coordinates":[8.4296300196465,49.484211928258]}}
And here my php code (updated Dec-2):
$qry = $pub->find(
array( '$and' =>
array(
array( 'geoLoc' =>
array('$nearSphere' =>
array('$geometry' =>
array('type'=>'Point',
'coordinates'=>
array(
floatval($sLon), floatval($sLat)
)
),
'maxDistance' => 500
)
)
),
array( '$or' =>
array(
array( 'name' => new MongoRegex("/.*club/i")),
array( 'name' => new MongoRegex("/.*zimm/i"))
)
),
array('$or' =>
array(
array('category_list.name' => 'Night Club'),
array('category_list.name' => 'Dance Club'),
array('category' => 'Club')
)
)
)
),
array('id' => 1, 'name' => 1, '_id' => 0)
);
Anyone know why the results are not limited to the specified maxDistance?

I found a similar issue on StackOverflow which outlines that one has to use radians for the maxDistance parameter.
See https://dba.stackexchange.com/questions/23869/nearsphere-returns-too-many-data-what-am-i-missing-am-i-wrong-is-it-a-bug-d
Also it is probably helpful if you'd test the query in mongo shell without using the PHP APIs first (just to see if the query is generally working and append '.explain()' to it to see what generally happens inside DB).

Related

Compound Indexes on MongoDB

Sorry for my english, I need help on mongodb indexes. I have a capped collection (size: 10GB) with some fields for my application logs.
Example structure: Logs[_id, userId, sum, type, time, response, request]. I have created compound index: [userId,time,type]. I get two arrays are grouped records by userId for today, where 'type' is "null" and "1". And my two query example:
$group = array(
array(
'$match' => array(
'userId' => $userId,
'time' => array(
'$gt' => date("Y-m-d")
),
'type' => array('$ne' => null)
)
),
array(
'$group' => array(
"_id" => '$userId',
"total" => array('$sum' => '$sum'),
"count" => array('$sum' => 1)
),
)
);
$results = $collections->aggregate($group);
$group = array(
array(
'$match' => array(
'userId' => $userId,
'time' => array(
'$gt' => date("Y-m-d")
),
'type' => 1
)
),
array(
'$group' => array(
"_id" => '$userId',
"count" => array('$sum' => 1)
),
)
);
$results2 = $collections->aggregate($group);
If current user has more 100000 documents on collection for today - the speed of my query is very slow (more 10 sec). Give me some advices on creating the right index, please :) Thanks.
Based on the explain that you posted, the correct index is being used (BtreeCursor), it is using only the index (i.e. it is a covered index query - indexOnly is true) and nothing is being matched (n = 0) in this case. So, that all checks out generally, though $ne as a clause in the first example is not going to be very efficient.
However the main issue based on the explain is likely the fact that the index does not appear to be fully in memory. There are 13 yields listed and the most common reason for a query like this to yield is when it has to fault to disk to page something in. Since, as mentioned previously, it is only using the index, those yields imply faults to disk for the index and hence indicate that the whole index is not in memory.
If you re-run the query immediately after this it should be faster (assuming the index can actually fit into available memory) because the index will have been paged in by the first run. If it is still slow on the second run and showing yields, then you either don't have enough memory to hold the index in memory or something else is evicting it from memory and you essentially have memory contention causing performance problems.

If column have a row=$variable and in another column on the same row=$variable2 return value 1

Sorry for confusing title. But I don't know how to explain this.(I don't speak that good English)
Here is a picture to make it alittle bit more clear:
http://img823.imageshack.us/img823/8812/edxt.png
If pic_name=328.jpg AND user=myhrmans return value of 1
else if you cant find value user=myhrmans return 0 for example. Anyway I can pull this off?
Thank you :)
Not sure to understand precisely what you mean, but I'm going to make an attempt.
Your query should be written somehow like this one:
SELECT user, pic_name,
IF(user = 'myhrmans' AND pic_name = '328.jpg', 1, 0) AS value
FROM yourtable
WHERE user = myhrmans;
When executed from PHP (and after fetching the values), your query will return this array:
$result = array(
0 => array(
'user' => 'myhrmans',
'pic_name' => '326.jpg',
'value' => 0
),
1 => array(
'user' => 'myhrmans',
'pic_name' => '329.jpg',
'value' => 0
),
2 => array(
'user' => 'myhrmans',
'pic_name' => '328.jpg',
'value' => 1
),
3 => array(
'user' => 'myhrmans',
'pic_name' => '319.jpg',
'value' => 0
)
);
Which, if I understood, approaches what you want.

Count too slow in MongoDB with PHP

I'm trying to check my code, with count lines. But this code works very slow. how can i optimize this code? is there anyway to count?
$find = $conn_stok->distinct("isbn");
for($i=0;$i<=25; $i++) {
$isbn = $find[$i];
$countit= $conn_kit->find(array('isbn'=>$isbn))->count();
if($countit> 0){
echo "ok<br>";
} else {
echo "error<br>";
}
}
Looks like you are trying to do a simple count(*) group by in the old SQL speak. In MongoDB you would use the aggregation framework to have the database do the work for you instead of doing it in your code.
Here is what the aggregation framework pipeline would look like:
db.collection.aggregate({$group:{_id:"$isbn", count:{$sum:1}}}
I will let you translate that to PHP if you need help there are plenty of examples available.
It looks like you're trying to count the number of 25 top most ISBNs used, and count how often they have been used. In PHP, you would run the following queries. The first one to find all ISBNs, and the second is an aggregation command to do the grouping.
$find = $conn_stok->distinct( 'isbn' );
$aggr = $conn_kit->aggregate(
// find all ISBNs
array( '$match' => array( 'isbn' => array( '$in' => $find ) ) ),
// group those
array( '$group' => array( '_id' => '$isbn', count => array( '$sum' => 1 ) ) ),
// sort by the count
array( '$sort' => array( 'count' => 1 ) ),
// limit to the first 25 items (ie, the 25 most used ISBNs)
array( '$limit' => 25 ),
)
(You're a bit vague as to what $conn_stok and $conn_kit contain and what you want as answer. If you can update your question with that, I can update the answer).

How to search in fulltext index using php in mongodb

I'm using mongodb 2.4 and added fulltext index to the "title" field in one the collection. How should I search something in that field using php?
This is the code I use right now:
$params = array(
'_id' => array(
'$gt' => (int)$gt
)
);
$r = $this->collection->find( $params )->limit($limit);
This seem to be the answer to my question:
<?php
$result = $db->command(
array(
'text' => 'bar', //this is the name of the collection where we are searching
'search' => 'hotel', //the string to search
'limit' => 5, //the number of results, by default is 1000
'project' => Array( //the fields to retrieve from db
'title' => 1
)
)
);
http://www.php.net/manual/en/mongodb.command.php#111891

search php assoc array (hash map) as mysql

example
public $inputs=array(
array( 'sysname'=>'pt_name','dbname' => 'users.name','label' => 'user (name/ID)','value' => '',
'type' => 'text','rules' => 'required','attr'=>'class="autocomplete"'),
array( 'sysname'=>'pt_dob','dbname' => 'users.dob','label' => 'Patient Dob','value' => '',
'type' => 'text','rules' => 'required','attr'=>'class="dob ac" Disabled'),
array( 'sysname'=>'pt_gender','dbname' => 'users.gender','label' => 'gender','value' => 'male,female',
'type' => 'dropdown','rules' => 'required','attr'=>'class="ac" Disabled'),
array( 'sysname'=>'visit_date','dbname' => 'visits.date','label' => 'Date','value' => '',
'type' => 'text','rules' => 'required','attr'=>'class="datepicker"'),
array( 'sysname'=>'visit_time','dbname' => 'visits.time_booked','label' => 'Time','value' => '',
'type' => 'text','rules' => 'required','attr'=>'class="timepicker"'),
array( 'sysname'=>'visit_type','dbname' => 'visits.type','label' => 'Visit type','value' => 'visit,schedule',
'type' => 'dropdown','rules' => 'required','attr'=>'')
);
how can i search this array for only arrays that have pt_ in its sysname for example ?
the idea is i have many types of rows all in same table, so instead of running a mysql query to fetch each type separatly example:
$pt=db->query("select * from table where sysname like 'pt_%'")->result();
$visit=db->query("select * from table where sysname like 'visit_%'")->result();
i want to fetch all at one and split them in php to decrease db load.
so how can i do this ? and is it worth it or better of keep my querys separate.
array_filter and a PHP-style closure* would be a pretty simple solution to this:
function buildFilter($key, $needle) {
return function($array) use($key, $needle) {
return (strpos($array[$key], $needle) !== FALSE);
};
}
$matches = array_filter($inputs, buildFilter('sysname', 'pt_'));
var_dump($matches);
NB: What PHP calls a "closure" is quite a bit different from what most other languages use for the same term, so please make sure to read the PHP documentation.
Doing a couple of queries is fine, your DB can handle that easily. If you're doing dozens of queries for dozens of types (each with only a few rows), it might be worth investigation moving that logic to PHP.
What I would recommend is to put the systype in a separate column with an index on it. That will speed of your query a lot and take load of your DB. Even better is you can make that column an ENUM.
public $inputs=array(
array( 'systype'=>'pt', 'sysname'=>'pt_name','dbname' => 'users.name','label' => 'user (name/ID)','value' => '',
'type' => 'text','rules' => 'required','attr'=>'class="autocomplete"'),
...
$pt=db->query("select * from table where systype = 'pt'")->result();
$visit=db->query("select * from table where systype = 'visit'")->result();

Categories