Archive for the ‘General’ Category

Oracle needs to get hurt .. badly!

Август 13th, 2010

There are plenty of bad things in the world. But I guess there is nothing that has such a drastic and dangerous impact on my life that software patents. If patents are good thing in other industries can be discussed, but in the software industry its a clear cut thing: they hurt innovation, they hurt small businesses and they scare the shit out of me. And if you are a software developer, they should scare the shit out of you too! Now Oracle decided as the first big company that is not just a patent troll to actually sue a company over software patents. heck maybe others have sued before, but lets stop this behavior right here right now. We must send a clear message. We must send a clear message to Oracle that they better stop. And we must send a clear message to any other company holding software patents that they better not think of suing. I have not thought this out all the way yet, but right now I am thinking the best form of protest would be if all of us open source developers strip out support for any Oracle product in our next release: Throw out support for BerkleyDB, MySQL, Oracle whatever. The business math must be clear: Suing over software patents will cost more then it can ever get in returns. Who is with me in organizing such a protest? Or does someone have a better idea of how to approach this?

Maybe I should clarify: The point is not to hurt any of the open source projects that would be affected by such a move. Stripping out the support for one release would of course be a nuisance for users, but it would not be a big issue in the long run. Of course if Oracle doesn't react we might need to continue or to turn up the heat. It might actually lead to a fork or two if Oracle decides to keep this up. In this case I am also hoping that this will cause a few OSS guys at Oracle to hand in their notices .. even more people willing and capable to lead forks. Anyway the stronger our immediate message the lower the chances we need to be doing anything for any extended period of time.


PlanetMySQL Voting: Vote UP / Vote DOWN

TaskFreak! v0.6.2 – Alter Search Plugin

Июль 15th, 2010

Background Knowledge


The Search Plugin for TaskFreak! created by DaDaemon and xdu v0.0.1 (March 26, 2007) was designed to create a simple, quick search capability of the tasks title and description. As well it only searched through he current task view (tasks visible at the time) and tasks that are not completed. For some this was not what was desired and would rather have the Search Plugin search through all tasks weather completed or not and as well search through the comments of tasks along with the title and description. I’ll show you how this is done using Searcher, bchristie and davidlmansfield instructions posted on the TaskFreak! Forums.

Solution – Add the Ability to Search All Tasks


Edit the “index.php” located in the root of TaskFreak! as follows. We will be working in the “Load Tasks” section to just above the “Task Order” section. This solution was posted by Searcher at Re: Quick ‘n’ Dirty Search Plugin.

Code Before
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
$arrFilters = array();
 
// --- filter: project ---
if ($pProject) {
    // load tasks for specific project
	$sqlFilter = 'ii.projectId = \''.$pProject.'\'';
    $pLink=Tzn::concatUrl($pLink,'sProject='.$pProject);
} else if (!$objUser->checkLevel(6)) {
    // user can only access his own projects
    if ($objUserProjectList->rMore()) {
        $arrProject = array();
        while($objTmp = $objUserProjectList->rNext()) {
            $arrProject[] = $objTmp->id;
        }
        if ($objUser->checkLevel(13)) {
        	$arrProject[] = '0';
        }
        $sqlFilter = 'ii.projectId IN ('.implode(',',$arrProject).')';
 
        unset($arrProject);
        $objUserProjectList->rReset();
    }
}
 
$objItemList->addWhere($sqlFilter);
 
// --- filter: user ---
$pUser = intval($_REQUEST['sUser']);
if (!isset($_REQUEST['sUser']) && @constant('FRK_DEFAULT_VIEW_OWN_TASKS')) {
	// default view is own tasks only
	$pUser = $objUser->id;
}
 
if($_REQUEST['sUser'] == 'myprojects' && $_SESSION['selUser']){
   $objItemList->addWhere('ii.authorId='.$_SESSION['selUser']);
}
elseif ($pUser) {
	$objItemList->addWhere('ii.memberId = \''.$pUser.'\'');
    $_SESSION['selUser'] = $pUser;
    $pDefaultUserId = $pUser;
} else {
    unset($_SESSION['selUser']);
    session_unregister('selUser');
    $pDefaultUserId = $objUser->id;
    // by default, show own tasks
    /*
    if (!$objUser->checkLevel(1)) { // admin can see all
	    $objItemList->addWhere('(ii.memberId='.$objUser->id
    		.' OR ii.authorId='.$objUser->id.')');
    }
    */
}
 
$pLink=Tzn::concatUrl($pLink,'sUser='.$pUser);
 
// --- private tasks --------------------------------------------------------
 
$arrFilter[] = 'showPrivate=0';
 
if ($objUser->checkLevel(12)) {
	// show internal tasks
	$arrFilter[] = 'showPrivate=1';
}
 
$arrFilter[] = '(showPrivate=2 AND (ii.memberId='.$objUser->id
	.' OR ii.authorId='.$objUser->id.'))';
 
$objItemList->addWhere('('.implode(' OR ',$arrFilter).')');
 
// --- filter: context ---
 
if ($_REQUEST['sContext']) {
	$pContext = Tzn::getHttp($_REQUEST['sContext'],true);
	$objItemList->addWhere('context = \''.$pContext.'\'');
    $pLink=Tzn::concatUrl($pLink,'sContext='.$pContext);
}
 
$sqlFilter = '';
$pShow = ($_REQUEST['show'])?$_REQUEST['show']:'today';
$pLink=Tzn::concatUrl($pLink,'show='.$pShow);
 
$pKeepNoDead = intval(@constant('FRK_NO_DEADLINE_KEEP') -1) * 86400;
 
// --- Filter per date -----------------------------------------------------
 
switch ($pShow) {
	case 'all':
		break;
	case 'future':
		// show coming tasks and late tasks (undone only)
		$sqlFilter = '((deadlineDate >= \''
			.strftime(TZN_DATE_SQL,PRJ_DTE_NOW).'\' AND statusKey < '
			.FRK_STATUS_LEVELS.')'.' OR statusKey < '.FRK_STATUS_LEVELS.')';
        // show uncompleted tasks with no deadline
		$sqlFilter .= ' OR (deadlineDate = \'9999-00-00\' AND statusKey < '
			.FRK_STATUS_LEVELS.')';
		break;
	case 'past':
		// show past tasks and already done
		$sqlFilter = '(deadlineDate < \''
			.strftime(TZN_DATE_SQL,PRJ_DTE_NOW).'\' OR statusKey = '
			.FRK_STATUS_LEVELS.')';
		break;
	case 'today':
		// show all future tasks (done + undone) and late tasks
		$pKeepNoDead = intval(@constant('FRK_NO_DEADLINE_KEEP') -1) * 86400;
		$sqlFilter = '(statusKey = '.FRK_STATUS_LEVELS.' AND statusDate > \''
			.gmdate('Y-m-d 00:00:00',time()-$pKeepNoDead).'\') ';
 
		// hide far future tasks ?
		$tmpFilter = '';
		if (@constant('FRK_DEFAULT_FAR_FUTURE_HIDE')) {
			$tmp = intval(FRK_DEFAULT_FAR_FUTURE_HIDE) * 86400;
			$tmpFilter .= 'deadlineDate < \''
				.gmdate('Y-m-d 00:00:00',time()+$tmp).'\'';
		}
 
		// show tasks with no deadline ?
		if (@constant('FRK_NO_DEADLINE_TOO')) {
			// yes
			if ($tmpFilter) {
				$sqlFilter .= 'OR ('.$tmpFilter
					.' OR deadlineDate = \'9999-00-00\')'
					. ' AND statusKey < '.FRK_STATUS_LEVELS;
			} else {
				$sqlFilter .= ' OR statusKey < '.FRK_STATUS_LEVELS;
			}
		} else {
			// don't show uncompleted non planned tasks
			if ($tmpFilter) {
				$sqlFilter .= ' OR ('.$tmpFilter.' AND statusKey < '
    	        	.FRK_STATUS_LEVELS.')';
			} else {
	            $sqlFilter .= ' OR (deadlineDate <> \'9999-00-00\' AND statusKey < '
    	        	.FRK_STATUS_LEVELS.')';
			}
		}
 
        if (@constant('FRK_DEFAULT_CURRENT_TASKS')) {
            $objItemList->setPagination(FRK_DEFAULT_CURRENT_TASKS);
        }
		break;
	default:
		break;
}
 
// -TODO- Add filter current project only (no completed, no cancelled)
 
// echo '<p>&</p><p>-</p><p>-</p>'.$sqlFilter;
 
if ($sqlFilter) {
	$objItemList->addDateFilter($sqlFilter);
}
 
// search filter
	$pSearch = ($_REQUEST['search']);
	if ($pSearch) {
		$sqlFilter = '(ii.title LIKE \'%'.$pSearch.'%\' OR ii.description LIKE \'%'.$pSearch.'%\')';
		$objItemList->addWhere($sqlFilter);
	}
Code After
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
$pSearch = ($_REQUEST['search']);
if ($pSearch) {
	$sqlFilter = '(ii.title LIKE \'%'.$pSearch.'%\' OR ii.description LIKE \'%'.$pSearch.'%\')';
	$objItemList->addWhere($sqlFilter);
}
else
{
	$arrFilters = array();
 
	// --- filter: project ---
	if ($pProject) {
	    // load tasks for specific project
		$sqlFilter = 'ii.projectId = \''.$pProject.'\'';
	    $pLink=Tzn::concatUrl($pLink,'sProject='.$pProject);
	} else if (!$objUser->checkLevel(6)) {
	    // user can only access his own projects
	    if ($objUserProjectList->rMore()) {
	        $arrProject = array();
	        while($objTmp = $objUserProjectList->rNext()) {
	            $arrProject[] = $objTmp->id;
	        }
	        if ($objUser->checkLevel(13)) {
	        	$arrProject[] = '0';
	        }
	        $sqlFilter = 'ii.projectId IN ('.implode(',',$arrProject).')';
 
	        unset($arrProject);
	        $objUserProjectList->rReset();
	    }
	}
 
	$objItemList->addWhere($sqlFilter);
 
	// --- filter: user ---
	$pUser = intval($_REQUEST['sUser']);
	if (!isset($_REQUEST['sUser']) && @constant('FRK_DEFAULT_VIEW_OWN_TASKS')) {
		// default view is own tasks only
		$pUser = $objUser->id;
	}
 
	if($_REQUEST['sUser'] == 'myprojects' && $_SESSION['selUser']){
	   $objItemList->addWhere('ii.authorId='.$_SESSION['selUser']);
	}
	elseif ($pUser) {
		$objItemList->addWhere('ii.memberId = \''.$pUser.'\'');
	    $_SESSION['selUser'] = $pUser;
	    $pDefaultUserId = $pUser;
	} else {
	    unset($_SESSION['selUser']);
	    session_unregister('selUser');
	    $pDefaultUserId = $objUser->id;
	    // by default, show own tasks
	    /*
	    if (!$objUser->checkLevel(1)) { // admin can see all
		    $objItemList->addWhere('(ii.memberId='.$objUser->id
	    		.' OR ii.authorId='.$objUser->id.')');
	    }
	    */
	}
 
	$pLink=Tzn::concatUrl($pLink,'sUser='.$pUser);
 
	// --- private tasks --------------------------------------------------------
 
	$arrFilter[] = 'showPrivate=0';
 
	if ($objUser->checkLevel(12)) {
		// show internal tasks
		$arrFilter[] = 'showPrivate=1';
	}
 
	$arrFilter[] = '(showPrivate=2 AND (ii.memberId='.$objUser->id
		.' OR ii.authorId='.$objUser->id.'))';
 
	$objItemList->addWhere('('.implode(' OR ',$arrFilter).')');
 
	// --- filter: context ---
 
	if ($_REQUEST['sContext']) {
		$pContext = Tzn::getHttp($_REQUEST['sContext'],true);
		$objItemList->addWhere('context = \''.$pContext.'\'');
	    $pLink=Tzn::concatUrl($pLink,'sContext='.$pContext);
	}
 
	$sqlFilter = '';
	$pShow = ($_REQUEST['show'])?$_REQUEST['show']:'today';
	$pLink=Tzn::concatUrl($pLink,'show='.$pShow);
 
	$pKeepNoDead = intval(@constant('FRK_NO_DEADLINE_KEEP') -1) * 86400;
 
	// --- Filter per date -----------------------------------------------------
 
	switch ($pShow) {
		case 'all':
			break;
		case 'future':
			// show coming tasks and late tasks (undone only)
			$sqlFilter = '((deadlineDate >= \''
				.strftime(TZN_DATE_SQL,PRJ_DTE_NOW).'\' AND statusKey < '
				.FRK_STATUS_LEVELS.')'.' OR statusKey < '.FRK_STATUS_LEVELS.')';
	        // show uncompleted tasks with no deadline
			$sqlFilter .= ' OR (deadlineDate = \'9999-00-00\' AND statusKey < '
				.FRK_STATUS_LEVELS.')';
			break;
		case 'past':
			// show past tasks and already done
			$sqlFilter = '(deadlineDate < \''
				.strftime(TZN_DATE_SQL,PRJ_DTE_NOW).'\' OR statusKey = '
				.FRK_STATUS_LEVELS.')';
			break;
		case 'today':
			// show all future tasks (done + undone) and late tasks
			$pKeepNoDead = intval(@constant('FRK_NO_DEADLINE_KEEP') -1) * 86400;
			$sqlFilter = '(statusKey = '.FRK_STATUS_LEVELS.' AND statusDate > \''
				.gmdate('Y-m-d 00:00:00',time()-$pKeepNoDead).'\') ';
 
			// hide far future tasks ?
			$tmpFilter = '';
			if (@constant('FRK_DEFAULT_FAR_FUTURE_HIDE')) {
				$tmp = intval(FRK_DEFAULT_FAR_FUTURE_HIDE) * 86400;
				$tmpFilter .= 'deadlineDate < \''
					.gmdate('Y-m-d 00:00:00',time()+$tmp).'\'';
			}
 
			// show tasks with no deadline ?
			if (@constant('FRK_NO_DEADLINE_TOO')) {
				// yes
				if ($tmpFilter) {
					$sqlFilter .= 'OR ('.$tmpFilter
						.' OR deadlineDate = \'9999-00-00\')'
						. ' AND statusKey < '.FRK_STATUS_LEVELS;
				} else {
					$sqlFilter .= ' OR statusKey < '.FRK_STATUS_LEVELS;
				}
			} else {
				// don't show uncompleted non planned tasks
				if ($tmpFilter) {
					$sqlFilter .= ' OR ('.$tmpFilter.' AND statusKey < '
	    	        	.FRK_STATUS_LEVELS.')';
				} else {
		            $sqlFilter .= ' OR (deadlineDate <> \'9999-00-00\' AND statusKey < '
	    	        	.FRK_STATUS_LEVELS.')';
				}
			}
 
	        if (@constant('FRK_DEFAULT_CURRENT_TASKS')) {
	            $objItemList->setPagination(FRK_DEFAULT_CURRENT_TASKS);
	        }
			break;
		default:
			break;
	}
 
	// -TODO- Add filter current project only (no completed, no cancelled)
 
	// echo '<p>&</p><p>-</p><p>-</p>'.$sqlFilter;
 
	if ($sqlFilter) {
		$objItemList->addDateFilter($sqlFilter);
	}
}

Solution – Add Ability to Also Search within Task Comments


Edit the “index.php” located in the root of TaskFreak! as follows. We will be working in just below the heading section of the “Load Tasks”. This solution was posted by davidlmansfield at [PATCH] [Search Plugin] include tasks matching in comment fields.

Code Before
39
40
41
42
43
44
// search filter
$pSearch = ($_REQUEST['search']);
if ($pSearch) {
	$sqlFilter = '(ii.title LIKE \'%'.$pSearch.'%\' OR ii.description LIKE \'%'.$pSearch.'%\')';
	$objItemList->addWhere($sqlFilter);
}
Code After
39
40
41
42
43
44
// search filter
$pSearch = ($_REQUEST['search']);
if ($pSearch) {
	$sqlFilter = '(ii.title LIKE \'%'.$pSearch.'%\' OR ii.description LIKE \'%'.$pSearch.'%\' OR ii.itemId in (select itemId from '.$objItemList->gTable('itemComment').' where body LIKE \'%'.$pSearch.'%\'))';
	$objItemList->addWhere($sqlFilter);
}

Solution – Stop SQL Injections


Edit the “index.php” located in the root of TaskFreak! as follows. We will be working in just below the heading section of the “Load Tasks”. This solution was posted by bchristie at Re: Quick ‘n’ Dirty Search Plugin.

Code Before
39
40
// search filter
$pSearch = ($_REQUEST['search']);
Code After
39
40
// search filter
$pSearch = isset($_REQUEST['search'])?mysql_real_escape_string($_REQUEST['search']):false;

PlanetMySQL Voting: Vote UP / Vote DOWN

TaskFreak! v0.6.2 – Add Unique Ticket/Task Number

Июль 14th, 2010

Background Knowledge


TaskFreak! does not show within the interface a unique ticket/task number. Depending on the use of TaskFreak! having such a value can have it’s benefits. Since this unique value does already exist in the back end, it will be fairly trivial to render this value within the task list and task details panel. I will show you how this is done using Searcher’s solution posted in the TaskFreak! Forum. There has been some modifications but not much.

Solution


  1. Edit index.php in the root of TaskFreak!. Add the table header column just below line # 248 as follows.
    2
    
    <th width="3%" onclick="freak_sort('itemId')" class="sortable" >< ?php echo $langForm['id']; ?></th>
  2. Next add the table data just below line # 302 as follows.
    2
    
    <td style="text-align:right;">< ?php echo $objItem->id; ?>&nbsp;</td>
  3. Edit /include/html/xajax_panel_view.php just below line # 5 just before the priority column as follows.
    6
    7
    8
    9
    10
    
    <div id="fid">
            	<div class="flabel">< ?php echo $GLOBALS['langForm']['id']; ?></div>
            	<div class="vid">< ?php echo $objTask->id; ?></div>
            < /div>
    </div>
  4. Edit /include/language/en/freak.php just below line # 46 as follows.

    Note: Each interface language file will be required to be edited if you desire to use them. If you do not do so, you will receive error messages and it may cripple TaskFreak!

    2
    
    'id' => 'ID',
  5. Edit applicable skins’ CSS in this example /skins/redfreak/css/freak.css just below line # 550.
    9
    10
    11
    12
    13
    14
    15
    16
    
    #fid 
    {
    	float: left;
    	width: 80px;
    	height:32px;
    	margin-left: 3px;
    	font-weight: bold;
    }
  6. Edit applicable skins’ CSS in this example /skins/redfreak/css/freak.css ID #fpro and remove “margin-left: 3px;”.

Source: Unique Ticket/ToDo Follow Number – Further prioritizing


PlanetMySQL Voting: Vote UP / Vote DOWN

Transforming end user queries to Solr

Июль 1st, 2010

A bit less than a year ago I last did a presentation about a telephone book application where we used SQL to do some fairly advanced filtering over about 30 tables of data. The app generated SQL statements that filled pages, the more terms the more pages, but on a 10k dataset it still came back within a few milliseconds, thanks to a ton of indexing and denormalization tricks (SQL Server is a lot more powerful here than MySQL) I had applied. Now in a more recent project I am dealing with 10M+ dataset running on MySQL and so decided to learn about Solr. Wow, that thing is amazing and way more flexible in terms of query language than I expected. As a result I do not see it any more for just projects that are too big for an RDBMS, but more as the way to do search in general. I have mentioned resolutionfinder.org a few times (used to be called UN-informed.org). Solr is a key piece there and more importantly I am looking to expand the use of Solr query language quite a bit. Actually for those who know, you can already do a lot more powerful queries, something Liip will be investing some more time to make more accessible to end users with some UI tweaks planned in July. But in this blog post I want to talk about a prototype class I threw together (Look ma', I'm using git!) by working ezcSearch to help me in parsing and transforming end user queries into complex Solr queries.

Right now we have 3 input boxes on the resolutionfinder.org start page. The first one lets user input terms which are searched in the document title and the clause content. Here I recently exposed some of the syntax supported by the dismax handler like "+" to require and "-" to prohibit terms. Next is an input box that doesn't directly affect the search filtering. What it does is use auto suggest to build up a list of tag to filter on. The third input box actually is a separate search form entirely which is used for searching for specific documents by document code. My vision is to bring these all together into one input box. So if someone wants to do a fulltext search they could do something like the following:

"security council" +africa tag:malaria code:A/RES/*

This query would be parsed and result in looking for documents that must contain "africa" in the document title or clause content (scoring those that also contain "security council" higher), which are tagged with "malaria" and who's document code started with "A/RES/". This requires quite a bit of transformation. Actually the final queries I want to construct looks like this:

q=tag_ids:23 AND document_code_prefix:(A/RES/) AND (_query_:"{!dismax qf='content document_title' pf='content document_title' mm=0 v=$qq}")&qq="security council" +africa

So what happened here? As you can see I transformed "tag" to "tag_ids" and "code" to "document_code_prefix". These are the internal fields I use in the Solr index. There is actually also a "document_code" field, which I would use if the code would not end with a "*". The "document_code_prefix" just copies the "document_code" and applies an ngram filter to make prefix searches super fast. Also you may notice that instead of "malaria" I am filtering for 23, this is because right now I just index the tag id's and not the names. The next bit is taking all the un-fielded filters and apply a dismax search handler on them. In order to not require fancy escaping I use "v=$qq" to define that the actual search terms are passed in the "qq" GET parameter.

With the above mentioned class this sort of thing becomes fairly simple. I am using the symfony sfSolrPlugin, which provides query construction tools via the sfLuceneCriteria object. All I have to do is use the parser to parse the end user query into tokens and then use a custom term class that uses the sfLuceneCriteria instance to handle the serialization.


<?php
$criteria = new sfLuceneCriteria;
$stack = array(new phpSolrQueryTermCustom('AND', $criteria));
$parser = new phpSolrQueryParser($factory);

$tokens = $parser->parse($q, $stack);
$terms = $parser->processTerms($tokens);
var_dump($q);
var_dump($terms->serialize());
?>

You can check out the examples to get some more details about what is possible. Following is the code to the example on github, but here is a link to the actual term class and action class used in production.


<?php
class phpSolrQueryTermCustom extends phpSolrQueryTerm {
    protected $criteria;

    public function __construct($op = '', $criteria = null)
    {
        $this->op = $op;
        $this->criteria = $criteria;
    }

    protected function processTerm($term) {
        $prefix = $term->prefix;
        $field = $term->field;
        $term = (string)$term;
        $op = $this->op;
        $op = rtrim(" $op ").' ';
        if (!empty($field)) {
            switch ($field) {
            case 'code':
                $field = substr($term, -1, 1) === '*' ? 'document_code_prefix' : 'document_code';
                $term = $field === 'document_code_prefix' ? substr($term, 0, -1) : $term;
                break;
            case 'tag':
                $field = 'tag_ids';
/*
                $q = Doctrine_Query::create()
                    ->select('t.id')
                    ->from('Tag t')
                    ->where('t.name = ?', array($term));
                $term = $q->execute(array(), Doctrine_Core::HYDRATE_SINGLE_SCALAR);
*/
                $term = rand(1, 100);
                break;
            default:
                throw new Exception('Unsupported field: '.$field);
            }
            $this->criteria->addField($prefix.$field, $term, $op, true);
            return;
        }
        $term = parent::processTerm($term);
        return $prefix.$term;
    }

    public function serialize()
    {
        $terms = $this->terms;
        $op = $this->op;
        $op = rtrim(" $op ").' ';
        $dismax = array();
        foreach ($terms as $key => $term) {
            if ($term instanceOf phpSolrQueryTerm) {
                $term = $this->processTerm($term);
                if (!is_null($term)) {
                    $dismax[] = $term;
                }
            } else {
                $dismax[] = parent::processTerm($term);
            }
        }
        if (!empty($dismax)) {
            $dismax = implode(' ', $dismax);
            $subcritieria = new sfLuceneCriteria;
            $subcritieria->add('_query_:"{!dismax qf=\'content document_title\' pf=\'content document_title\' mm=0 v=$qq}"', $op, true);
            $this->criteria->add($subcritieria, 'AND');
            $this->criteria->addParam('qq', $dismax);
        }

        $q = $this->criteria->getParams();
        $q['q'] = array($this->criteria->getQuery());
        return $q;
    }
}
?>

PlanetMySQL Voting: Vote UP / Vote DOWN

Deploying app updates to a cluster

Июнь 21st, 2010

So William was asking on twitter how to best deploy symfony apps to a cluster of servers. There are actually some nice deployment tools inside the symfony cli that ease deployment to a single server, but that doesn't really cover the cluster case. Actually I assume that if you have a cluster of servers the best deployment strategy should probably be optimized against your specific use case. But let's make this question a bit more general: How do you deploy updates to your PHP apps running a clustered setup? What architecture do you pick? How do you keep the site running with as few limitations as possible during the update? How do you distribute the new code? How do you clean and prime your caches? How do you handle DB changes? How do you ensure that the DB and code changes do not get in the way of each other?

Obviously the choice of RDBMS can play a big role here. MySQL likes replication setups, so I can envision making the site read only, disabling replication, update master schema, take a slave out of the load balancer, update code, reenable replication to that slave, repeat. With PostgreSQL, Oracle and RDBMS like that you tend to scale with just getting a bigger DB server (and having a warm failover server). Guess that simplifies things a bit (especially since PostgreSQL even supports transactional DDL, which should help with timing the deployment of the DB changes). Or maybe just make sure that DB changes never cause BC breaks.

Anyway, I am looking to just collect approaches here in the comments. So please ideally post links to slides or detailed blog postings below. I am sure this could become a very useful link collection for the PHP community. Thanks :)


PlanetMySQL Voting: Vote UP / Vote DOWN

Cool Web Designer is Looking for Work

Июнь 18th, 2010

My wife – a good web designer with 6 years of experience with web design, HTML and CSS is looking for a job. Here is some information about her:

We’re physically located in Toronto, Canada, but she has a great experience of working remotely too. So, if you need a web designer or a junior web designer, feel free to contact Tanya.



PlanetMySQL Voting: Vote UP / Vote DOWN

Vendors: please stomp out SQL injection

Июнь 12th, 2010

I have blogged about prepared statements a few times, which is what most people rely on (too much) for SQL injection protection. I say too much because they [ do not really protect code fully against SQL injection attacks and they come with a lot of performance hurting baggage. To sum up: prepared statements do not handle all aspects of dynamic SQL creation, they add network I/O and memory overhead and they tend to generate less optimal query plans. Some of these issues can be solved by doing client side emulation, but that brings with itself its share of issues and I have to agree with Bill and not Brian that parsing SQL should be left to the server.. So vendors, how about it? How about offering us a proper solution to prevent SQL injection attacks like I was asking for in my last post on the topic?

Again here are the requirements:

  • a single round trip for a single execution (prepare and execute with one command)
  • no overhead for single execution (option to not return a handle for additional executions, option to optimize the query plan for the first set of values)
  • handle scalar placeholders, as well as lists of scalar values (so also handle stuff like IN)
  • handle identifiers and operators (so for example to handle dynamic ORDER BY)
  • easy debugging (some way to return the final generated query)

Doesn't seem too hard to implement. I like how Oracle does a lot of things here and maybe they will teach some of that to MySQL now that its "their baby". I like the ":name" syntax for the placeholders, but more importantly the fact that they generate the query plan for the first set of values. However recently I found myself using the question mark syntax more and more, even though PDO can switch between the two versions seamlessly (well the parser has its share of issues). Maybe for other types (identifiers and placeholders) another syntax could be used like (exclamation mark maybe?) to ensure that one does not accidentally enable more flexibility in a parameter than intended.


PlanetMySQL Voting: Vote UP / Vote DOWN

CLI, Roller, Jersey, JavaOne… and More GlassFish News – April 27th, 2010

Апрель 28th, 2010

Install and Run Apache Roller 4.01 on GlassFish and OpenSolaris
Dave Koelmeyer has posted Detailed Instructions on how to install Apache Roller 4.01 on GlassFish v2.1 using MySQL 5.1 for storage.  He uses OpenSolaris snv_134, the subject of a tea-leaf-reading thread.

Slides and Code Samples on Jersey and JAX-RS
The Slides and code from Paul Sandoz's presentation at Presentation at AlpesJug on Jersey, JAX-RS and Atmosphere are now now available.  The actual presentation was in French, but the slides are in English, and the code is... code.

Invoke OSGi Service from JAX-WS Endpoint
Arun has published yet another TOTD (Tip Of The Day), with complete instructions and code.  This one is  TOTD #130: Invoking a OSGi service from a JAX-WS Endpoint. Arun's approach is to document the demos he gives at his presentations through the TOTDs.  Quite a bit of work, but it makes the content useful to a world-wide audience.

WAS V7 - Inching Towards JavaEE 6
IBM has recently been using a "Feature Pack" approach in upgrading its WebSphere AppServer; it seems to work pretty well for them and they released two packs for WAS V7: Feature Pack for OSGi and JPA 2.0 and Feature Pack for SCA.  IBM is, of course, one of the Java Licensees; WAS v7 is one of the JavaEE 5 Compatible App Servers, the feature pack aproach helps it move towards the JavaEE 6 list.

VirtualBox at Oracle
One of the challenges during Hands-On-Labs is setting up: the attendees usually bring their own laptops but each of them is different and requires slighlty different setup.  Asking for prep work before attending is not always successful.  A solution now being used in some DB HOLs at Oracle is to Use VirtualBox. Which is the same approach that both Arun and Alexis had advocated for a new series of GlassFish HOLs being planned.

GlassFish CLI
Masoud has a detailed post - actually a book chapter - that you should read to Learn the GlassFish v3 Command Line Administration Interface (CLI)

JavaOne 2010
This year's JavaOne is the first under Oracle and will coincide with Oracle OpenWorld.  Some things will be different, but others are mostly the same - including how the content is being selected - see Sharat Chander's interview by Tori Wieldt for some answers; others will evolve as we get closer to the event.


PlanetMySQL Voting: Vote UP / Vote DOWN

Drizzle Developer Day is TODAY!

Апрель 16th, 2010

http://drizzle.org/wiki/Drizzle_Developer_Day_2010

Upstairs in the Hyatt right near the Speaker room (down the hallway on the left from the main conference registration desk).

See you here!


PlanetMySQL Voting: Vote UP / Vote DOWN

Gimme a schema for the schema-less

Апрель 10th, 2010

One of the key features of NoSQL is the fact that its schema-less. Awesome. Of course I could just dump a serialized string of my "document" into an RDBMS and I could end up with more or less the same, but the big difference of course is that NoSQL (to me key-value stores do not fall under the NoSQL umbrella) still supports non hacky ways to interact with individual values inside a document as well as indexing. But while at first it might seem great to not have at the database enforce a specific schema, the app developer better have a good idea of his schema. Otherwise one developer might call a field "is_active" the next one might call it "isActive" and another one "enabled". I have little to no experience with CouchDB, MongoDB etc. but I am not really all that thrilled about schema-less for the above reason, what I want is no-cost-for-schema-changes, I do want a schema!

This is why I was quite thrilled back when IBM come out with top level XML support in DB2 Viper. Basically in DB2 you can store your documents as XML strings. You can query and manipulate these XML strings right inside the database and you can even define indexes to speed up access. But and here comes the big but for me, you could also optionally define an XML schema that the given XML needs to adhere to. IIRC DB2 supports storing the XML string as is or in an optimized (binary?) representation. I never really used DB2 as it didn't really felt like the answer to the web problem, but I wish MySQL or PostgreSQL would provide similar capabilities. Maybe I should give DB2 a second look, their express edition is much less restricted compared to Oracle's offering too. Also I wonder if any of the NoSQL stores support validating documents against some centrally managed data structure definition, aka a schema, of course with no cost for making changes to that schema.

PS: I tweeted about this a few days ago, but the Doctrine developer team is looking for people to help out with writing a driver.


PlanetMySQL Voting: Vote UP / Vote DOWN