Perform case-insensitive lookup on an Array in MongoDB? - php

So, I've decided to get my feet wet with MongoDB and love it so far. It seems very fast and flexible which is great. But, I'm still going through the initial learning curve and as such, I'm spending hours digging for info on the most basic things.
I've search throughout the MongoDB online documentation and have spent hours Googling through pages without any mention of this. I know Mongo is still quite new (v1.x) so it explains why there isn't much information yet. I've even trying looking for books on Mongo without much luck. So yes, I've tried to RTM with no luck, so, now I turn to you.
I have an Array of various Hashtags nested in each document (ie: #apples, #oranges, #Apples, #APPLES) and I would like to perform a case-insensitive find() to access all the documents containing apples in any case. It seems that find does support some regex with /i, but I can't seem to get this working either.
Anyway, I hope this is a quick answer for someone.
Here's my existing call in PHP which is case sensitive:
$cursor = $collection->find(array( "hashtags" => array("#".$keyword)))->sort(array('$natural' => -1))->limit(10);
Help?

I suspect your query is not returning what you really want...
If you do
db.col.find( { some_array_field: ["item1", "item2"] } );
then it will only match documents that have EXACTLY these two items in some_array_field. So if there is a document with [item1, item2] hashtags it will match, but a document with [item1, item2, item3] tags won't match.
You could use the $all argument as described in this post:
How do you do an AND query on an array in mongodb?
e.g.
db.col.find( { some_array_field: { $all : ["item1", "item2"] } } );
or:
db.col.find( { some_array_field: "item1", some_array_field: "item2" } );
This distinction of complete-document-match and partial-match was really confusing in MongoDB for me at first.

here is an example for case insensitive search in mongodb with php
$search_string='baR';
$searchQuery = array(
'$or' => array(
array(
'field1' => new MongoRegex("/^$search_string/i"),
),
array(
'field2' => new MongoRegex("/^$search_string/i"),
),
)
);
$cursor = $customers->find($searchQuery);
Hopes this help any.

Related

Efficient way to capture "variations" or "combinations" ore "aliases" for switch-case argument(s) in PHP

I am pretty sure this challenge has been solved by someone already but even searching with different words, I could not find a solution for this problem:
I try to give users the possibility to run certain functions of a class based on an argument like
service_class::do_this( "selection-argument" );
but the user shall be able to use "clear words" as well as "aliases" and even "well known" abbreviations or synonyms.
I use switch-case construction to call the "real" function.
Example: To get the contens of a folder, The user can use "getdir", "dir", "Directory", "getfolder", "getcontent", "content", "d-cont" and a number of more other "matching words" to start the function(s) underlaying and getting back the very same result.
Capture-ing lowercase/uppercase is simple. What I search for is an efficient way to capture all possible "variations" - that are, of course different number of variations for different functions called.
At the moment I use multiple "case "": lines after each other, but that makes the code quite long, and further I would like the user to be able to "enahnce" the recognition set for a certain function.
That's why I thought about "stripos" to determine first what "internal word" to use and only then run into the switch-case construction.
Anyone had that issue and can direct me to a "good and efficient" solution?
Seems that Stck-exchange itself had a similar challenge (https://codereview.stackexchange.com/tags/php/synonyms) ... maybe I can simply re-use the underlying code?
Thanks in advance and sorry if I overlooked a solution already posted.
You could use a database or array. Let's do the latter. So to determine whether an user wants to get a directory you would define an array like this:
$getDirVariants = ['getdir',
'dir',
'directory',
'getfolder',
'getcontent',
'content',
'd-cont'];
It is easy to add more of these arrays. To test the query word you would do:
$queryWord = strtolower($queryWord);
if (in_array($queryWord, $getDirVariants)) service_class::getDir(<arguments>);
elseif (in_array($queryWord, $deleteVariants)) service_class::delete(<arguments>);
You can easily add to the arrays or make it a 2D array to contain more commands. That array could also be placed in a database.
Especially when there are many commands, with many variants, a database will be the better solution, because you can find the query word with one database query.
There's a variation I can think of that will also simplify the code when there are many commands. You could use an associative array to find the command:
$commandVariants = ['getdir' => 'getdir',
'dir' => 'getdir',
'directory' => 'getdir',
'getfolder' => 'getdir',
'getcontent' => 'getdir',
'content' => 'getdir',
'd-cont' => 'getdir',
'delete' => 'delete',
'del' => 'delete',
'remove' => 'delete',
'unlink' => 'delete'];
$queryWord = strtolower($queryWord);
if (isset($commandVariants[$queryWord])) {
$command = $commandVariants[$queryWord];
service_class::$command(<arguments>);
}
else echo "I don't recognize that command.";
This uses a variable identifier.

add an array to a multi-dimensional array using PHP

I have been searching for a simple answer, but it just doesn't exist, or I don't know how to ask the right questions, to gain the right answers.
Here is the simple to understand question:
I have a big multi-dimensional array, as illustrated here::
$dataFile = array (
array(
array(
data => 'infohere_01',
)
),
array(
data => 'infohere_02',
)
),
etc etc say up to infohere_125 and beyond...
I want to BREAK up the individual arrays creating a simple to manage .php files - using stupid logic ie KISS I thought this would do it. By using include, then stepping through all the seperate .php snippets to make a BIGGER array...
<?php
$dataFile = array (
include '/datafile_01.php';
include '/datafile_02.php';
);
?>
etc etc to include up to and above '/datafile_125.php';
with each include '/datafile_01.php';
being just::
<?php
array(
array(
data => 'infohere_01',
)
),
?>
Not the most elegant way to do it - as it doesn't work...
I cannot find a simple solution. I find complex solutions that do not apply to a simple need.
OK, sorted it now using KISS
Keeping it stupid simple!
Breaking it down into the basics, you find a solution - the complexity of 3d arrays, causing complexity itself, but with additional errors between 2d/3d coding using ), and/or ); there was a compounding of errors going on.
The 2 comments made, both gave a indication towards solving the problem, using RETURN and a loop to include the includes, but the real gem came from further research.
I quote:
Thanks for your help everyone. I found the answer, and I feel pretty
silly: Adding to 3D arrays is as easy as adding to regular arrays!
$users[] = $newuser;
That's bloody it! Amazing, huh? If $users is a 3D array, and $newuser
is an array, it just adds it to the 3D array like I need it to. When
all else fails, try the simplest thing you can think of! Oh well, the
5-6 hours I spent researching this
answered some questions I'll need to take care of later, and I
learned a ton while at it.
Source: [http://www.v7n.com/forums/coding-forum/49651-php-appending-multidimensional-arrays.html][1]
OK so what have I created::
Simple 2D arrays for each file
that is then created by adding to a 3d array as follows!
$shop = array();
$shop[] = include 'Catalogue_ARRAYS/Fire-and-Rescue-Tools_2009.php';
$shop[] = include 'Catalogue_ARRAYS/Insulated-Digging-Tools_2013.php';
Simple really - typical KISS
The 2d arrays / Include.php files are constructed as follows, using RETURN array( rather than $foo = array(::
NOTE:: the use of ); rather than ), which was the MISTAKE!
<?php
return array(
array(
data => 'infohere_01',
)
**);**
?>
That's it SOLVED!
Thanks to all of you who have contributed. We where nearly alright, but sods-law a mini mistake here and there with ' and ; kills things dead.
Thanks again
If you can have some variable declaration inside each of your include '/datafile_01.php':
<?php
$foo = array(
array(
data => 'infohere_01',
)
),
?>
Then you would be able to merge all your include files with something like this, and thus building your big array:
<?php
$dataFile = array();
foreach($includeFiles as $file)
{
include($file);
$dataFile[] = $foo;
}
?>
Included data file should contain:
<?php
return array(
array(
data => 'infohere_01',
)
);

Incorrectly used array_multisort() giving correct result?

I'm currently touching some legacy code in an attempt to clean it and have come across something of a puzzle. It's not even remotely mission critical that I resolve it, so consider this a sporting question at most, but it's still perplexing me. Colour me curious.
This code:
// dummy data for the sake of testing / abstract visualisation;
// x is thrown in as an experiment:
$workTables = array(
array('x' => 5, 'Name' => 'foo3', 'Update_time' => '2013-04-04 04:40',),
array('x' => 4, 'Name' => 'foo4', 'Update_time' => '2013-04-01 04:40',),
array('x' => 3, 'Name' => 'foo2', 'Update_time' => '2013-04-04 09:40',),
array('x' => 2, 'Name' => 'foo1', 'Update_time' => '2013-04-12 04:40',),
array('x' => 1, 'Name' => 'foo5', 'Update_time' => '2012-12-04 04:40',),
);
// original legacy code:
if (!empty($workTables)) {
$sort = array();
foreach ($workTables as $key => $value) {
$sort[$key]["Update_time"] = $value["Update_time"];
}
array_multisort($workTables, SORT_ASC, SORT_STRING, $sort);
}
...while throwing twelve notices ("Notice: Array to string conversion ..."), works as expected, by ordering all elements in $workTables according to Update_time.
(The 'x' column is part of my attempt to rule out that it has anything to do with Name, which in the original code is a fixed prefix followed by a timestamp (e.g. work_table_1367940392, work_table_1367940395, ...), making sorting by Name equivalent to sorting by Update_time.)
Obviously, since I don't want to program by coincidence, at the very least this is going to be replaced with:
// new code:
if (!empty($workTables)) {
$sort = array();
foreach ($workTables as $key => $value) {
$sort[$key] = $value["Update_time"];
}
array_multisort($sort, SORT_ASC, SORT_STRING, $workTables);
}
...which conforms to the description of array_multisort(), also does what we want, and doesn't throw Notices in our face.
But what I'm really interested in is why the old code works (notices notwithstanding).
A partial reason seems to be the behaviour of asort() and co., which have the (partly) undocumented feature that they can work with multi-dimensional arrays by acting according to the contents of the array, working through the structure from "left to right" (at least in PHP 5.4.7 (cli))... but... I'm stuck trying to comprehend what's making $workTables and $sort 'interchangable'.
I've tried to look at PHP's C source code to figure it out, but I'm stuck at trying to understand what happens here:
/* Do the actual sort magic - bada-bim, bada-boom. */
zend_qsort(indirect, array_size, sizeof(Bucket **), php_multisort_compare TSRMLS_CC);
...since my knowledge of C has rusted terribly and zend_qsort() itself is just flatly out of my league.
Any takers?
Keep in mind I'm not desperate for an answer, so don't sink too much time into this, but maybe someone else likes a puzzle, too? :)
Personally, I've invested some time into this purely because I prefer understanding code thoroughly, especially when I'm trying to clean it up - even if it's code that works only by coincidence. I've just reached a dead-end when it comes to further comprehension, so stackoverflow seemed like the best chance for further enlightenment.
So, if you have an idea about what's going on behind the scenes (I suspect it's something trivial I've overlooked; that tends to be my problem after going around in circles for a while), I'd love to hear it! :)
From what I understand, an array is being created and stored in $sort[$key]. Then, once you call array_multisort(), it happens to convert the array back into a string and sort using that string as the value that defines the order -- just as the warning says. Then, this string is used as the order-defining value.
How is this conversion array->string performed? Just doing echo $anArrayValueHere; is not helpful; it prints out Array. Similar trick, echo "" . $anArrayValueHere . ""; also doesn't yield anything useful. (But neither does it yield a notice!)
It's quite possible that serialize() is used. Presuming that, let's see what would get used as a order-defining value:
#!/usr/bin/php5
<?php
$sort[0]["Update_time"] = '2012-12-04 04:40';
echo serialize($sort[0]);
then
$ ./php tmp.php
a:1:{s:11:"Update_time";s:16:"2012-12-04 04:40";}
It looks like the key would be sufficiently definitive to yield something usable to define sort order just as well as just the Update_time value would.
I'm not sure that serialize() is used, but if it is, I think this is the explanation.

mongodb conversation system

I'm implementing a very simple conversation system on mongodb.
The idea should be that when I'm opening a convo, it should display send and received messages. It's OK so far and should be pretty easy, by using a simple query like this pseudocode:
(from "my_id" AND to "friend_id") OR (from "friend_id" AND to "my_id")
this should be pretty straightforward and simple, but querying just looks so complicated to me with mongodb (I'm coming from mysql).
I'm trying this, but it's not working at all, and can't find out where the error is.
$cursor =$collection->find
(
array('$or' =>
array('$and' => array("from"=>"$profile", "to"=>"$loggeduser")),
array('$and' => array("to"=>"$profile", "from"=>"$loggeduser"))
)
)->limit(50)->sort(array('date' => -1));
this returns nothing.... Where's the mistake?
Thanks in advance.
Take a look at this page on how to do advanced MongoDB queries: http://www.mongodb.org/display/DOCS/Advanced+Queries
You can use a combination of the $and and $in operators to get what you need. Using the mongo shell, your query would look something like this:
db.yourCollectionName.find({$and: {from: {$in: ["toUser", "loggedOnUser"]}}, {to: {$in: ["toUser", "loggedOnUser"]}}})
I believe this may also give you the equivalent:
db.yourCollectionName.find({$and: {$or: [{from: "toUser"}, {to: "toUser"}]}}, {$or: [{from: "loggedOnUser"}, {to: "loggedOnUser"}]}}})
From there it's a matter of converting the above to the language/DSL that you're using, and sorting by date.
In your code, you don't need the ($and => array()) wrapping each of the objects that you're trying to find. Remove them, so it looks like this:
$cursor = $collection->find(
array('$or' =>
array(
array("from"=>"$profile", "to"=>"$loggeduser"),
array("to"=>"$profile", "from"=>"$loggeduser")
)
)
) ->limit(50)->sort(array('date' => -1));

MongoDB PHP query using $and operator on the same object

I've got a fairly simple query that I have working in command line, and am trying to execute using php.
It's looking for documents that match all of the given "tags" entered in a search box:
db.collection.find( { $and: [ { tags: "cats" }, { tags: "video" } ] } )
I can't seem to figure out how to translate this to php. I've been using codeigniter for everything up to this point (Alex Bilbie's library), but have looked into building my own queries with no luck. Most of the methods I've tried eliminate the first tag (cats), since it is looking at the same field name (tags).
any thoughts?
PHP can be a bit tricky with how you need to format the arrays. What I've found to be the best way to create the queries is through doing things like:
json_encode($myQuery);
then comparing that to what actually works directly on the console of the app. In this case you're looking for:
$item = array('$and' => array(array('tags' => 'cats'), array('tags' => 'videos')))
which you can confirm by doing:
echo(json_encode(array('$and' => array(array('tags' => 'cats'), array('tags' => 'videos')))));
Good Luck!

Categories