{"id":185,"date":"2009-01-24T01:31:36","date_gmt":"2009-01-23T22:31:36","guid":{"rendered":"http:\/\/yeti.albascout.ro\/blog\/?p=185"},"modified":"2009-01-24T01:31:36","modified_gmt":"2009-01-23T22:31:36","slug":"news-search-engine-proiect-ci","status":"publish","type":"post","link":"https:\/\/yeti.albascout.ro\/blog\/news-search-engine-proiect-ci\/","title":{"rendered":"News Search Engine \/ Proiect CI"},"content":{"rendered":"<p>CI este singura materie de an 5 pe care o fac to\u0163i studen\u0163ii, indiferent de specializare (though, \u00een func\u0163ie de ce specializare ai, ai un alt profesor). Presupun materia asta are numele pe care \u00eel are ca s\u0103 ofere flexibilitate \u015fcolii \u00een a pune acolo con\u0163inutul pentru care are profesori la un moment dat, but this is just a wild guess. Motivul care m\u0103 face s\u0103 zic asta este c\u0103 materia se ocup\u0103 cu Information Retrieval, \u015fi numele ei n-are-a face cu asta.<\/p>\n<p>Anyway, \u00eent\u00e2mplarea fericit\u0103 a f\u0103cut ca specializ\u0103rile C1 \u015fi C2 (eu sunt la C1) s\u0103 \u00eel aib\u0103 ca profesor pe first-time appeareance <a href=\"http:\/\/www.l3s.de\/~chirita\/\">Paul Chiri\u0163\u0103<\/a>, care e un meseria\u015f \u00een domeniu \u015fi care a lucrat pe la tot soiu de meseria\u015fi (like Yahoo) \u015fi care acum s-a \u00eentors \u00een Bucure\u015fti \u015fi lucreaz\u0103 la Adobe. Anyway, cursu a fost interesant, am \u015fi reu\u015fit s\u0103 merg la c\u00e2teva, dar era <strong>lunea, la 8, \u00een Leu<\/strong>, ceea ce nu este prea compatibil cu mine :).<\/p>\n<p>Am ales, \u00eempreun\u0103 cu Gia, Micvs \u015fi Miki s\u0103 alegem calea interesant\u0103 \u015fi s\u0103 facem un proiect (alternativa era un referat). Evident, materia a \u00eenceput \u00een octombrie, noi am aproximativ ales tema prin noiembrie \u015fi am avut prima \u00eent\u00e2lnire pe 17 decembrie, cand deadline-ul era 12 ianuarie. Tema pe care ne-am ales-o a fost un <strong>news search engine<\/strong>, adic\u0103 partea de crawl (spidering), partea de indexare, partea de gestiune a queryurilor, \u015fi chiar \u015fi o interfa\u0163\u0103 de test.<\/p>\n<p>Contribu\u0163ia mea la proiect nu a fost at\u00e2t de mare pe c\u00e2t mi-a\u015f fi dorit, \u015fi am ajuns la Bucure\u015fti la aproape o s\u0103pt\u0103m\u00e2n\u0103 dupa ce Micvs \u015fi Gia s-or apucat deja de treab\u0103, dar totu\u015fi sper ca am ajutat :).<\/p>\n<p>So, un pic despre proiectul nostru: ne-am propus sa re-invent\u0103m roata ca s\u0103 ne d\u0103m seama cam cum era f\u0103cut\u0103, a\u015fa c\u0103 p\u0103r\u0163ile esen\u0163iale le-am facut de la 0. Motorul nostru de c\u0103utare de \u015ftiri este scris \u00een Java, \u015fi folose\u015fte RMI \u00eentre diferitele module (web-crawler, indexer, clien\u0163i) \u015fi JDBC cu baza de date \u00een care men\u0163inem un cache. Practic, ce ne-am pornit la drum s\u0103 facem a fost o reinventare a ro\u0163ii (sau, ca s\u0103 citez din ceva ce-am citit pe net: ce rost are s\u0103 te apuci s\u0103 reinventezi roata, hai s\u0103 reinvent\u0103m motorul &#8211; de c\u0103utare :P).<\/p>\n<p><strong>Crawlerul <\/strong>nostru este antrenat acum pe dou\u0103 site-uri: <a href=\"http:\/\/news.bbc.co.uk\">bbc.co.uk<\/a> \u015fi <a href=\"http:\/\/theguardian.co.uk\">theguardian.co.uk<\/a>. Am mers pe con\u0163inut \u00een englez\u0103 pentru c\u0103 e mai u\u015for de g\u0103sit, mai corect format \u015fi stufos dec\u00e2t ce g\u0103seam \u00een rom\u00e2n\u0103, dar mai ales pentru c\u0103 anumite componente ale unui SE (<a href=\"http:\/\/en.wikipedia.org\/wiki\/Stemming\">stemming<\/a>, spre exemplu) func\u0163ioneaz\u0103 ca \u00een teorie c\u00e2nd le aplici pe limba englez\u0103. Practic, un &#8222;spider&#8221; porne\u015fte de la site-ul principal, \u015fi g\u0103se\u015fte link-uri c\u0103tre categoriile principale ale site-ului, de unde ob\u0163ine linkuri c\u0103tre \u015ftiri. Se extrage textul \u015fi titlul \u015ftirii din pagin\u0103 (cu o expresie regulat\u0103, pentru fiecare site), \u015fi se trimite, printr-un apel RMI, server-ului de indexare. Dup\u0103 ce termin\u0103 toate linkurile, se semnalizeaz\u0103 server-ului c\u0103 s-a \u00eencheiat parcurgerea site-urilor \u015fi crawler-ul este apelat din nou dup\u0103 c\u00e2teva ore.<\/p>\n<p><strong>Server<\/strong>&#8211;<strong>ul de indexare <\/strong>\u00eentre\u0163ine, evident :), un <a href=\"http:\/\/en.wikipedia.org\/wiki\/Inverted_index\">inverted index<\/a>. Index-ul este reprezentat \u00een memorie ca un hashtable, \u00een care cheile sunt hashuri (int) ale cuvintelor trecute printr-un stemmer simplu de limb\u0103 englez\u0103, \u015fi valorile sunt liste \u00eenl\u0103n\u0163uite de referin\u0163e la documente \u015fi un vector de apari\u0163ii (pozi\u0163ii \u00een document \u00een care apare cuv\u00e2ntul respectiv). De fapt, se men\u0163ine c\u00e2te un vector de apari\u0163ii pentru titlu, con\u0163inut \u015fi sumar.<\/p>\n<p>\u00cen timp ce prime\u015fte toate \u015ftirile dintr-o &#8222;transmisie&#8221;, serverul folose\u015fte un pachet numit <a href=\"http:\/\/classifier4j.sourceforge.net\/\">classifier4j<\/a> pentru a calcula asem\u0103n\u0103rile \u00eentre dou\u0103 texte pe care le prime\u015fte. Dac\u0103 asem\u0103n\u0103rile sunt prea mari ( &gt; 95%), \u015ftirea este considerat\u0103 duplicat\u0103 \u015fi se ignor\u0103. Dac\u0103 \u015ftirea seam\u0103n\u0103 \u00een propor\u0163ie &gt; 80% cu un text, se consider\u0103 ca este despre acela\u015fi eveniment sau aceea\u015fi serie de evenimente ca \u015fi \u015ftirea cu care este comparat\u0103, astfel f\u0103c\u00e2ndu-se <a href=\"http:\/\/en.wikipedia.org\/wiki\/Cluster_analysis\">clustering<\/a> bazat pe evenimente. Compara\u0163ia se face folosind un motor care se folose\u015fte de <a href=\"http:\/\/en.wikipedia.org\/wiki\/Vector_space_model\">teorema cosinusului \u00eentr-un vector space de cuvinte<\/a> (classifier4j mai poate folosi un comparator bazat pe <a href=\"http:\/\/en.wikipedia.org\/wiki\/Bayes%27_theorem\">teorema lui bayes<\/a>, dar nu \u015ftiam despre ce era vorba \u015fi era mai complicat de folosit pentru ce voiam noi). La indexing-time se mai calculeaz\u0103 \u015fi un sumar static al textului, reprezentat din cele mai importante dou\u0103 propozi\u0163ii din text (calculate \u015fi ele folosind un algoritm bazat pe teorema cosinusului aplicat\u0103 pe spa\u0163iul documentului respectiv). La fiecare indexare, cache-ul este \u00eenlocuit (\u00een mod sincronizat) cu cache-ul nou.<\/p>\n<p>Aspectul la care micvs a insistat la construirea indexului a fost performan\u0163a: pars\u0103ri minime ( = 1) a oric\u0103rei liste, oric\u0103rui string, \u015fi optimiz\u0103ri pentru tratare de queryuri, \u00een detrimentul performan\u0163ei index\u0103rii, dac\u0103 a fost cazul.<\/p>\n<p><strong>Pentru tratarea query-urilor<\/strong>, server-ul prime\u015fte de la aplica\u0163iile client o list\u0103 de cuvinte cheie, printr-un apel RMI. Serverul folose\u015fte inverted index-ul pentru a g\u0103si documentele care con\u0163in cuvintele respective, \u015fi aceste liste sunt apoi intersectate. Fiecare document prime\u015fte o not\u0103, la query-time, bazat\u0103 pe num\u0103rul de apari\u0163ii a cuv\u00e2ntului \u00een textul documentului \u015fi pe existen\u0163a sau absen\u0163a cuv\u00e2ntului c\u0103utat \u00een titlu \u015fi \u00een sumarul documentului. Dac\u0103 c\u0103utarea se face dup\u0103 mai multe cuvinte, nota se acord\u0103 \u015fi \u00een func\u0163ie de apropierea \u00een cadrul documentului \u00eentre cuvinte (adic\u0103 e mai relevant un document care contine termenii de c\u0103utare mai apropia\u0163i &#8211; \u00een special unul l\u00e2ng\u0103 cel\u0103lalt).<em><\/em><\/p>\n<p>Pentru prezentare, miki a lucrat la o interfa\u0163\u0103 \u00een java, a\u015fa, de <a href=\"http:\/\/en.wikipedia.org\/wiki\/Proof_of_concept\">POC<\/a> (proof of concept) ca s\u0103 putem s\u0103 ar\u0103t\u0103m cum se ob\u0163in rezultatele \u015fi cum se grupeaz\u0103. Pe mine m-a ros nonstop faptu c\u0103 nu am f\u0103cut ceva webbased pentru c\u0103utare, c\u0103 pentru mine f\u0103cea mai mult sens s\u0103 fie webbased, a\u015fa c\u0103, dup\u0103 predarea proiectului, m-am jucat un pic, pentru prima dat\u0103 ever cu ni\u015fte Servlet-uri. Promit c\u0103 dac\u0103 o s\u0103 chiar termin ceva o s\u0103 le pun la mine pe site s\u0103 le poat\u0103 testa cine vrea :).<\/p>\n<p>Oricum, a fost un proiect instructiv, \u015fi, pe c\u00e2t \u00eemi displace mie Java, trebuie s\u0103 recunosc c\u0103 munca \u00een echip\u0103 e mult mai clar definit\u0103 c\u00e2nd se programeaz\u0103 \u00een limbaje gen Java. Proiectul \u0103sta a fost \u015fi primul proiect la care am reu\u015fit s\u0103-i conving pe ceilal\u0163i s\u0103 folosim SVN pentru controlul codului, ceea ce pentru mine a fost o reu\u015fit\u0103 personal\u0103.<\/p>\n<p>Partea mai pu\u0163in reu\u015fit\u0103 a fost prezentarea efectiv\u0103 a proiectului, unde am mers direct dup\u0103 o noapte nedormit\u0103 cu o prezentare \u00eencropit\u0103 \u015fi un proiect semi-finalizat. M-am b\u00e2lb\u00e2it nonstop la prezentare, \u015fi c\u00e2nd i s-o terminat la Gia bateria de la laptopu de pe care prezentam I froze. Evident, se pare c\u0103 prezent\u0103rile astea erau oarecum mai pu\u0163in colegiale \u015fi mai mult &#8222;oficiale&#8221; a\u015fa ca mi-am luat bob\u00e2rnacu necesar peste nas, dar \u00eel meritam pentru c\u0103 am mers, \u00eentr-adev\u0103r nepreg\u0103tit pentru o prezentare.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>CI este singura materie de an 5 pe care o fac to\u0163i studen\u0163ii, indiferent de specializare (though, \u00een func\u0163ie de ce specializare ai, ai un alt profesor). Presupun materia asta are numele pe care \u00eel are ca s\u0103 ofere flexibilitate \u015fcolii \u00een a pune acolo con\u0163inutul pentru care are profesori la un moment dat, but &hellip; <a href=\"https:\/\/yeti.albascout.ro\/blog\/news-search-engine-proiect-ci\/\" class=\"more-link\">Continu\u0103 s\u0103 cite\u0219ti <span class=\"screen-reader-text\">News Search Engine \/ Proiect CI<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[557],"tags":[727,646,651,698,705],"class_list":["post-185","post","type-post","status-publish","format-standard","hentry","category-facultate","tag-cs-pub","tag-information-retrieval","tag-java","tag-search-engine","tag-svn"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/yeti.albascout.ro\/blog\/wp-json\/wp\/v2\/posts\/185","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/yeti.albascout.ro\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/yeti.albascout.ro\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/yeti.albascout.ro\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/yeti.albascout.ro\/blog\/wp-json\/wp\/v2\/comments?post=185"}],"version-history":[{"count":0,"href":"https:\/\/yeti.albascout.ro\/blog\/wp-json\/wp\/v2\/posts\/185\/revisions"}],"wp:attachment":[{"href":"https:\/\/yeti.albascout.ro\/blog\/wp-json\/wp\/v2\/media?parent=185"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/yeti.albascout.ro\/blog\/wp-json\/wp\/v2\/categories?post=185"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/yeti.albascout.ro\/blog\/wp-json\/wp\/v2\/tags?post=185"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}