Страницы

понедельник, 29 ноября 2010 г.

Построение множеств в реляционной базе данных

Человеческий мозг интересная штука, особенно интересно наблюдать, как ты работаешь, напряженно думаешь, строишь огромные схемы, чтоб решить какую-то задачу, а потом вдруг озарение и в результате 50 строк предыдущего кода заменяются на 10 нового. Данная заметка родилась примерно так. Поговорим об организации простых математических множест в реляционной базе данных.
Множество это набор элементов, если по простому. Первое, что нужно нам иметь возможность делать - это получать список элементов входящих в множество.

Потребность в решении задачи возникло при составлении списка взаимозаменяемых автозапчастей. Потому первое решение( к сожелению) было примерно таким:

create table crosses( p_key1 int(11), p_key2 int(11) )
Где p_key1 и p_key2 соответственно номера деталей, которые эквивалентны. Если "деталь1" может заменить "деталь2", а "деталь2" может заменить "деталь3", тогда логично предпложить, что "деталь1" может заменить "деталь3", это называется транзитивностью, это не обязательная характеристика элементов множеств, но в нашем примере имеет место. Собственно для эти трех деталей потребуется как минимум две записи. Чтобы охарактеризовать связь между всеми тремя, но это теория. А на практике, чтобы не путаться в запросах, потому что таких деталей может быть больше десятка, вроде бы стало удобно для каждой записи хранить ее кросы. То есть для примера из трех деталей мы получили бы следующий вид :
p_key1 p_key2
"деталь1" , "деталь2"
"деталь1" , "деталь3"
"деталь2" , "деталь1"
"деталь2" , "деталь3"
"деталь3" , "деталь2"
"деталь3" , "деталь1"
Уже настараживает...А теперь введем еще пару факторов такие как
1) Оператор ошибается, и добавляет неправильные элементы в существующие множества
2) Множества эквивалентных элементов всегда пополняется.
И теперь сразу видна неповоротливость данной схемы, что порадило априори кучу ошибок в логике работы, которые подтвердились на практике. Да и количество записей растет со скоростью n*(n-1), где n - количество деталей.
Второе решение сильно напоминающее экзоскелет. Написать переделать таблицу экивалентных деталей на манер экселя то есть :

create table crosses( p_key1 int(11), p_key2 int(11),p_key3 int(11), p_key4 int(11) .... )
Где p_key1, p_key2,p_key3 , эквивалентные детали. Размерность такой таблицы вызывает сомнения, потому что эквивалентных деталей может быть просто дикое количество, даже цифра в 100 уже вызывает сильные опасения. Приведем минусы
1) Добавление одного элемента уже в существующее множество приведет к операции сравнения по ста столбцам в худшем случае.
2) не будем же мы делать в самом деле таблицу из ста столбцов, мы напишем класс или библиотеку, которая работает с несколькими таблицами подобного типа, это нам позволит вроде бы избежать ограничения с размерностью.
3) явно видно что писать придеться снова много, ошибки неизбежны...

Собственно просто решение пришло, когда я обдумывал и набирался смелости взяться за описанные выше случай. Собственно идея в присвоение каждому множеству определенного идентификатора. Таблица приобретает вид


create table crosses(p_set int(11), p_key int(11) )
Где p_set уникальный идентификатор множества. Алгоритм работы прост. Мы нашли "деталь1", которая эквавалента другой "детале2". Мы начинаем поиск в таблице

SELECT p_set as p_set1 FROM crosses WHERE p_key='деталь1'

SELECT p_set as p_set2 FROM crosses WHERE p_key='деталь2'

Если первый и второй запрос дал результат, то мы на, например, заменяем p_set1 на p_set2 соответственно . То есть произошло слияние множеств.

UPDATE crosses SET p_set= $p_set1 WHERE p_set=$p_set2


Если какой из запросов дал результат, например нам стало известно $p_set2 то добавление нового свойства сводится к

INSERT INTO crosses(p_key,p_set) VALUES('деталь1',$p_set2)

Если оба запроса не дали результата, то создаем новый идентификатор например

LOCK TABLE crosses WRITE;

SELECT max(p_set)+1 FROM crosses
INSERT INTO crosses(p_set,p_key) VALUES($new_set,'деталь1'), ($new_set,'деталь2')
UNLOCK TABLES


Выборка всех элементов множества, в которое входит данный элемент осуществляется
в итоге всегда в фиксированое количество опеаций - две. Например дла "деталь1"

SELECT p_set as s FROM crosses WHERE p_key='деталь1'

SELECT p_key FROM crosses WHERE p_set=$s


P.S
можно и одним запросом...Но так нагляднее

воскресенье, 28 ноября 2010 г.

пятница, 19 ноября 2010 г.

Пишем plugin для FireFox

Давно интересовала эта, но все руки не доходили, вернее не было идеи плагина. Но а потом пришлось, так сказать. Вообще написание плагина мне доставило удовольствие сравнимое с изучением фреймворка Django. Технология написания плагина для fireFox интуитивно понятный процесс. Для быстрого старта вап понадобиться зайти вот сюда :

https://addons.mozilla.org/en-US/developers/tools/builder

По ссылке вы получите простешую балванку для своего плагина, которую можно установить, у которой есть страница настройки и можно даже добавить кнопку в контекстном меню. Удобная штука, которая позволяет сократить много времени, и вы сразу же получите наглядный пример перед глазами. Дальше содержимое полученного мной архива:

chrome chrome.manifest defaults install.rdf

./chrome:
content locale skin

./chrome/content:
about.xul adding.xul ff-overlay.js ff-overlay.xul options.xul overlay.js

./chrome/locale:
en-US

./chrome/locale/en-US:
about.dtd adding.dtd options.dtd overlay.dtd overlay.properties

./chrome/skin:
overlay.css

Содержимое папки зависит от выбранных вами опций, мне нужна была кнопка в контекстном меню и настройки. В моем примере мне нужно была отдельная кнопка в
контексном меню браузера. Для того чтоб зарегистрировать что либо объекте браузера, нужно "проверлеить" его основной объект "chrome://browser/content/browser.xul". За это отвечает файл chrome.manifest, приведу пример моего
content ext_add chrome/content/
skin ext_add classic/1.0 chrome/skin/
locale ext_add en-US chrome/locale/en-US/

overlay chrome://browser/content/browser.xul chrome://ext_add/content/ff-overlay.xul

Где ext_add/content/ff-overlay.xul содержит следующее :

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="chrome://ext_add/skin/overlay.css" type="text/css"?>
<!DOCTYPE overlay SYSTEM "chrome://ext_add/locale/overlay.dtd">
<overlay id="ext_add-overlay" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script src="overlay.js"/>
<script src="ff-overlay.js"/>

<stringbundleset id="stringbundleset">
<stringbundle id="ext_add-strings" src="chrome://ext_add/locale/overlay.properties"/>
</stringbundleset>

<popup id="contentAreaContextMenu">
<menuitem id="context-ext_add" label="&ext_addContext.label;"
accesskey="&ext_addContext.accesskey;"
insertafter="context-stop"
oncommand="ext_add.onMenuItemCommand(event)"/>
</popup>


</overlay>


Добавить что-то другое можно, воспользовавшись стандартной документацие XUL.
С первых минут написания плагина возникнет вопрос, как получить доступ к текущему документу - через псевдоним текущего окна 'content' , например

var e =content.document.createElement('div');


Расскажем про диалоговые окна, не юзать же нам один promt и alert. В firefox-e в качестве шаблона используют XML-подобный язык XUL . Например пишем файлик типа adding.xul




<!DOCTYPE dialog SYSTEM "chrome://ext_add/locale/adding.dtd">
<dialog width='300' title="&about;" orient="vertical" autostretch="always" onload="sizeToContent()"
buttons="accept,cancel"
ondialogaccept="return ext_add.post2();"
ondialogcancel="return ext_add.cancel_post();"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script src="overlay.js">

<groupbox align="center" orient="horizontal">
<vbox>
<label control="texttitle">&title.label;</label>
<textbox id="texttitle">
<separator class="thin">
<label control="cattitle">&cattitle.label;</label>
<menulist id="'cattitle'">
<menupopup>
<menuitem label="IT-сфера" value="'9'/">
<menuitem value="19" label="'Авторы'">
</menupopup>
</menulist>
<separator class="thin">
</vbox>
<groupbox align="center" orient="horizontal">
<caption label="'&desc.label;'/">
<textbox multiline="true" rows="'5'" id="'desc'">
</groupbox>

</groupbox>
</dialog>



Код вызова диалога :
var win = window.openDialog( "chrome://ext_add/content/adding.xul",
"addingWindow", "chrome,centerscreen",
{text_data: this.commit_text,
main: this,
hst:content.window.location.host,
url:content.window.location.href });







Первый параметр указывает относительный путь к файлу дескриптору adding.xul ,
второй параметр - наименование окна,
третий - позиционирование,
далее идут параметры, которые мы передаем в диалоговое окно.
К параметрам можно обратиться следующим образом :

var plugin=window.arguments[0].main;
var h=window.arguments[0].hst;
var l=window.arguments[0].url;


Стандартная документация тут

Обратите внимание в adding.xul -в теге dialog описывается наличие кнопок OK, Cancel
а так же их обработчики через атрибуты ondialogaccept, ondialogcancel,
которые находяться в файле overlay.js.
Обработчик ondialogaccept :
post2 : function(e){
var cat=document.getElementById('cattitle').value;
var t=document.getElementById('texttitle').value;
var desc=document.getElementById('desc').value;
var txt=window.arguments[0].text_data;
txt=txt.replace(" "," ");
txt=txt.replace("","");
txt=txt.replace("&","");
txt=txt.replace("&","");
txt=''+txt+'';
var plugin=window.arguments[0].main;
var h=window.arguments[0].hst;
var l=window.arguments[0].url;
var params='cat_id='+cat;
params+='&text='+txt;
params+='&title='+t;
params+='&desc='+desc;
params+='&host='+h;
params+='&url='+l;

plugin.send_http(plugin.post_url,params,plugin.debug_func);
return true; },

Одним из важных элементов, которые вам понадобяться в начале написание плагина это ваши настройки...
Диалоговое окно настроек описавается тоже при помощи XUL.
Интересней, как получить сохраненные настройки :
//находим компонент настроек
var prefs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService);
// находим ветку относящуюся к нашему плагину в данном случае 'ext_add'
prefs = prefs.getBranch("extensions.ext_add.");
// получение настроек
login=prefs.getCharPref("loginpref");
pwd=prefs.getCharPref('pwdpref');
// проверка наличия настроек
if(!prefs.prefHasUserValue("loginpref")) {
alert("Заполните данные для авторизации");
return; }

Более детально читаем тут
Для любого приложения важна среда программирования, в данном случае удобно сделать отдельный профайл FireFox например Debug. Здесь подробно написано как это сделать под разными OS, в Linux-е я же просто запустил команду
" /usr/bin/firefox -no-remote -P dev". В новом профайле советую установить несколько плагинов для разработки.

Extension Developer's Extension
набор утилит javascript-shell, regexp evaluater, архиватор плагинов и т.п
Firebug - анализатор DOM -модели и не только
Tamper - дампер сетевых соединений, хорош, если вы активно используете Ajax.

Кстати про Ajax, из плагина можно обращаться к сторонним доменам,
если запросы выполняются из контекста вашего плагина.
Ajax работает через обычный XMLHttpRequest.


P.S Вообщем все интутивно понятно и знакомо, что не может не радовать.

среда, 17 ноября 2010 г.

Люблю python :)

Захотелось мне распарсить документ html, порылся я в мануалах наткнулся на пару
модулей и выбрал HTMLParser. Скажу сраз до меня не сразу доперло, что надо сделать класс, унаследовав его от HTMLParser сделать что-то типа этого
from HTMLParser import HTMLParser

class MyHTMLParser(HTMLParser):

def handle_starttag(self, tag, attrs):
print "Encountered the beginning of a %s tag" % tag

def handle_endtag(self, tag):
print "Encountered the end of a %s tag" % tag
А потом запустить его

p=MyHTMLParser()
p.feed('some text here')
Все хорошо, написал я свой класс, запустил, и получил что-то типа этого
HTMLParser instance has no attribute '[тут какой-то его внутренний атрибут]'

Гугл ответа не дал , а потом до меня дошло. Вообщем не надо переопределять конструктор класса, то есть если вы напишите примерно следующее

from HTMLParser import HTMLParser

class MyHTMLParser(HTMLParser):
def __init__(self,d,f)
self.d='ss'
self.f='dd'

def handle_starttag(self, tag, attrs):
print "Encountered the beginning of a %s tag" % tag

def handle_endtag(self, tag):
print "Encountered the end of a %s tag" % tag
Вы начнете получать ту белеберду, думаю вы уже догадались в чем проблема - все дело в методе __init__ , который, как я понял , переопределяет базовый со всеми его атрибутами. Потому там надо добавить вызов super().

пятница, 12 ноября 2010 г.

Сравнение FireFox и Microsoft IE. Или почему FireFox лучше :)

Так получилось, что понадобилось мне написать плагин для FireFox. Для сравнения решил глянуть как подобный плагин, или если быть точным будет писаться для IE. Полемику
разводить бессмысленно, главную мысль можно выразить просто приведением примера кода обоих плагинов. Например для FireFox -а он бы выглядел так :

  1. var test = {
  2. onload: function(aEvent){
  3. var doc = aEvent.originalTarget;
  4. if (doc instanceof HTMLDocument && doc.location.href != "about:blank" && !doc.defaultView.frameElement) {
  5. var s = doc.createElement('script');
  6. s.type = 'text/javascript';
  7. doc.body.appendChild(s);
  8. s.src = 'script.js';
  9. }
  10. }
  11. }
  12. window.addEventListener("load", function(){
  13. var appcontent = document.getElementById("appcontent"); if (appcontent) {
  14. appcontent.addEventListener("DOMContentLoaded", test.onload, true);
  15. }
  16. }, false);
Интуитивно понятная всем DOM -модель.. или вот код из моего плагина :


send_http : function(u,params,func){
var HttpMethod='POST';
var R=this.REQ;
if(R)
{
R.onreadystatechange=func;
R.open(HttpMethod,u,true);
R.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
R.send(params);
}else{
alert("I cant create browser");
}




},

get_http : function() {

if(window.XMLHttpRequest){

this.REQ=new XMLHttpRequest();
return this.REQ;
}else{
alert("sorry,change you browser please");
return null;
}





},
То есть всем хорошо знакомый старый JavaScript и написание диалогов при помощи XUL - подмножества XML. А вот что примерно мы увидим в IE с ее COM - моделью:

Тут происходит привязка к событию DISPID_DOCUMENTCOMPLETE, и в случае возникновения выполняем код:
  1. VARIANT vFlags = { 0 };
  2. VariantInit(&vFlags);
  3. vFlags.vt = VT_I4;
  4. vFlags.intVal |= navNoReadFromCache;
  5. BSTR testUrl = SysAllocString(L"javascript: код инъекции");
  6. mWebBrowser2->Navigate(testUrl, &vFlags, NULL, NULL, NULL);

Тут мы видим что-то абсолютно новое, непонятное и далекое от нас если мы незнакомы с документацией COM. Итог "без комментариев"

среда, 10 ноября 2010 г.

Разбиение чисел на разряды в php

Зашел я статистику блога, и увидел там, что на блог зашло
аж ...секунду 12 пользователей по запросу :
"php regexp разбиения числа на разряды"

Вообщем дорогие php-шники, помогу, чем могу...люблю помогать людям
есть такая прекрасная функция number_format в php, которой нет к сожелению в perl - сказывается разница в возрасте...

$number= 1234.56;

// английский формат (по умолчанию)
$english_format_number = number_format($number);
// 1,234

// французский формат
$nombre_format_francais = number_format($number, 2, ',', ' ');
// 1 234,56

$number = 1234.5678;

// английский формат без разделителей групп
$format_number = number_format($number, 2, '.', ' ');
// 1 234.57

?>

скопипастено отсюда - http://www.php.ru/manual/function.number-format.html

P.S
Кликнете на рекламу, не будь злюкой...я кофе хочу

понедельник, 8 ноября 2010 г.

Инновации, что учить начинающему специалисту

Согласно новости , мы наблюдаем новый виток развития индустрии. Так что дорогие студенты, в ближайшее время надо срочно начинать учить Android и сопутствующие сервисы.
Очень хорошие статья для так называемого "квик старта "
http://habrahabr.ru/blogs/android/99323/

http://habrahabr.ru/blogs/android/98704/

Удачи всем.
p.s
Новость радует, потому что политика гугла, имхо полиберальней, чем гламурного яблока.

Google и mod_pagespeed

Оно само не ничего не сжимает

http://code.google.com/intl/en/speed/page-speed/docs/using_m...

Могут перестать работать сложные js скрипты, например CKEditor.

Из личного опыта простейшая страница поиска с ajax стала дико тормозить, чего раньше не наблюдалось.

Итого незачет однозначно...

p.s

И вообще почему не использовать nginx в режиме проксирования.