From: Dobrica Pavlinusic Date: Thu, 9 Dec 2010 16:38:20 +0000 (+0100) Subject: Merge branch 'master' of github.com:dpavlin/angular-mojolicious X-Git-Url: http://git.rot13.org/?p=angular-mojolicious.git;a=commitdiff_plain;h=26fd4cefb008db630132c12b600d86760324f5f2;hp=e9c2a275a3b3adf5e3f3d0af6e6ae0142ced6485 Merge branch 'master' of github.com:dpavlin/angular-mojolicious Conflicts: public/app/casopisi2010/upitnik.html --- diff --git a/README b/README index afebd1b..b7af6eb 100644 --- a/README +++ b/README @@ -26,14 +26,23 @@ At it's current stage it provides support for angular $resource get, query and $ http://angularjs.org/Service:$resource +Data can also be serve static json files stored in: + + public/json/:database/:entity/:key + +which can be used to provide data using external stand-alone scripts. + + + Replication of data between instances using angular REST API can be done with: # create local CouchDB database - curl -X PUT http://localhost:5984/test + $ curl -X PUT http://localhost:5984/demo + {"ok":true} - ./angular-replicate.pl \ + $ ./angular-replicate.pl \ http://dpavlin.getangular.com/data/conference \ - http://localhost:3000/data/test + http://localhost:3000/data/demo Replication is currently good only for initial import of data since it doesn't support incremental replication and dies if data is allready present. @@ -41,18 +50,18 @@ support incremental replication and dies if data is allready present. Installation: - git submodule init - git submodule update + $ git submodule init + $ git submodule update Optionally build angular to get single file download - cd public/angular - rake compile - cd - + $ cd public/angular + $ rake compile + $ cd - Start it with: - ./angular-server.pl daemon --reload + $ ./angular-server.pl daemon --reload Angular examples available: @@ -61,12 +70,47 @@ Angular examples available: - template/conferece - conference submission example using mojolicious REST API server - public/app/conference - new application layout with latest example confernce submission - curl -X PUT http://localhost:5984/conference/_design/symposium \ + $ curl -X PUT http://localhost:5984/conference/_design/symposium \ -d @public/app/conference/_design/symposium + +CouchDB examples: + + couchdb-changes.pl - simple _changes feed watcher using Mojo::Client documented at + + http://wiki.apache.org/couchdb/HTTP_database_API#Changes + + + couchdb-trigger.pl - FSM document with hook for user-defiend triggers + + trigger/shell.pm - execute shell commands + trigger/email.pm - skeleton for sending e-email + trigger/KinoSearch.pm - full-text search + + $ curl -X PUT http://localhost:5984/demo + {"ok":true} + + $ ./couchdb-trigger.pl http://localhost:5984/demo trigger/shell.pm + + $ curl -X PUT http://localhost:5984/demo/t1 -d '{"trigger":{"command":"notify-send CouchDB t1"}}' + + + couchdb-external-kinosearch.pl - external searcher for KinoSearch indexes + (configuration for CouchDB is included at end of file) + + $ ./couchdb-trigger.pl http://localhost:5984/demo trigger/KinoSearch.pm + + $ curl -X PUT http://localhost:5984/demo/text -d '{"text":"foobar bla bla"}' + {"ok":true,"id":"text","rev":"1-cf9bb608f93af7f4e5e40656a6e50096"} + + $ curl 'http://localhost:5984/demo/_kinosearch?include_docs=true;q=foobar' + + + Roadmap: + implement angular-server.pl which implements REST API supported by $resource in angular + persistency to local CouchDB, and use views to query data ++ implement CouchDB _changes and FSM inside document as base for queue or triggers - tests diff --git a/couchdb-external-kinosearch.pl b/couchdb-external-kinosearch.pl new file mode 100755 index 0000000..b455bfd --- /dev/null +++ b/couchdb-external-kinosearch.pl @@ -0,0 +1,70 @@ +#!/usr/bin/perl +use warnings; +use strict; + +# http://wiki.apache.org/couchdb/ExternalProcesses +# +# curl 'http://localhost:5984/drzb2011/_kinosearch?q=a&include_docs=true' + +use KinoSearch::Search::IndexSearcher; +use Mojo::JSON; +use Data::Dump qw(dump); + +$|=1; + +our $json = Mojo::JSON->new; + +open(my $log, '>>', '/tmp/couchdb-external-kinosearch.log'); + +while() { + warn "# $_\n"; + my $request = $json->decode($_); + print $log "<<< $_\n"; + + my $response = { + code => 200, +# json => {}, + }; + + if ( my $q = $request->{query}->{q} ) { + + my $searcher = KinoSearch::Search::IndexSearcher->new( + index => '/tmp/kinosearch.' . $request->{info}->{db_name}, + ); + + my $hits = $searcher->hits( query => $q ); + + $response->{json}->{total_hits} = $hits->total_hits; + + while ( my $hit = $hits->next ) { + my $r = { + _id => $hit->{_id}, + _rev => $hit->{_rev}, + score => $hit->get_score, + }; + $r->{doc} = $json->decode( $hit->{doc} ) if exists $request->{query}->{include_docs}; + push @{ $response->{json}->{hits} }, $r; + } + + } else { + $response->{json}->{error} = "no query found"; + } + + my $send = $json->encode($response); + print $send, $/; + print $log ">>> $send\n"; +} + + +__END__ +; insert following into /etc/couchdb/local.ini: + +[log] +level = debug + +[external] +kinosearch = /srv/angular-mojolicious/couchdb-external-kinosearch.pl + +[httpd_db_handlers] +_kinosearch = {couch_httpd_external, handle_external_req, <<"kinosearch">>} + diff --git a/couchdb-trigger.pl b/couchdb-trigger.pl index 02f5c32..1724b67 100755 --- a/couchdb-trigger.pl +++ b/couchdb-trigger.pl @@ -25,6 +25,9 @@ my ( $url, $trigger_path ) = @ARGV; $url ||= 'http://localhost:5984/monitor'; $trigger_path ||= 'trigger/shell.pm' ; +our $database = $1 if $url =~ m{/(\w+)/?$}; + +sub commit { warn "# commit ignored\n"; } require $trigger_path if -e $trigger_path; my $seq = 0; @@ -45,12 +48,9 @@ while( ! $error ) { $tx->res->body(sub{ my ( $content, $body ) = @_; - debug 'BODY' => $body; + return if length($body) == 0; # empty chunk, heartbeat? - if ( length($body) == 0 ) { - warn "# empty chunk, heartbeat?\n"; - return; - } + debug 'BODY' => $body; foreach ( split(/\r?\n/, $body) ) { # we can get multiple documents in one chunk @@ -83,7 +83,7 @@ while( ! $error ) { if ( $tx->res->code == 409 ) { info "TRIGGER ABORTED started on another worker? ", $tx->error; } else { - info "ERROR ", $tx->error; + info "ERROR $url/$id ", $tx->error; } } else { my $res = $tx->res->json; @@ -97,7 +97,7 @@ while( ! $error ) { $client->put( "$url/$id" => $json->encode( $change->{doc} ) => sub { my ($client,$tx) = @_; if ($tx->error) { - info "ERROR", $tx->error; + info "ERROR $url/$id", $tx->error; } else { my $res = $tx->res->json; $change->{doc}->{_rev} = $res->{rev}; @@ -114,6 +114,8 @@ while( ! $error ) { } + commit; + }); $client->start($tx); diff --git a/public/app/casopisi2010/upitnik.html b/public/app/casopisi2010/upitnik.html index 584e6f5..2c24b05 100644 --- a/public/app/casopisi2010/upitnik.html +++ b/public/app/casopisi2010/upitnik.html @@ -44,7 +44,10 @@ Ako želite, upišite svoju e-mail adresu: X -dodaj još jedno polje +dodaj još jedno polje

2. Odaberite i obrazložite svoj izbor: diff --git a/trigger/KinoSearch.pm b/trigger/KinoSearch.pm new file mode 100644 index 0000000..6866c35 --- /dev/null +++ b/trigger/KinoSearch.pm @@ -0,0 +1,81 @@ +use KinoSearch::Index::Indexer; +use KinoSearch::Plan::Schema; +use KinoSearch::Analysis::PolyAnalyzer; +use KinoSearch::Plan::FullTextType; + +# Create a Schema which defines index fields. +my $schema = KinoSearch::Plan::Schema->new; +my $polyanalyzer = KinoSearch::Analysis::PolyAnalyzer->new( + language => 'en', +); +my $type = KinoSearch::Plan::FullTextType->new( + analyzer => $polyanalyzer, +); +my $blob_type = KinoSearch::Plan::BlobType->new( stored => 1 ); +my $string_type = KinoSearch::Plan::StringType->new; +$schema->spec_field( name => '_id', type => $string_type ); +$schema->spec_field( name => '_rev', type => $string_type ); +$schema->spec_field( name => 'doc', type => $blob_type ); + +# Create the index and add documents. +our $indexer; + + +sub _indexer { + $indexer ||= KinoSearch::Index::Indexer->new( + schema => $schema, + index => "/tmp/kinosearch.$database", + create => 1, + ); +}; + +sub flatten { + my ($flat,$data,$prefix) = @_; + if ( ref $data eq '' ) { + $$flat->{$prefix} .= "\n" . $data; + $$flat->{$prefix} =~ s/^\n//; # strip first + } elsif ( ref $data eq 'HASH' ) { + foreach my $key ( keys %$data ) { + my $full_prefix = $prefix ? $prefix . '.' : ''; + $full_prefix .= $key; + flatten( $flat, $data->{$key}, $full_prefix ); + } + } elsif ( ref $data eq 'ARRAY' ) { + foreach my $el ( @$data ) { + flatten( $flat, $el, $prefix ); + } + } elsif ( ref $data eq 'Mojo::JSON::_Bool' ) { + $$flat->{$prefix} = $data; + } else { + die "unsupported ",ref($data)," from ",dump($data); + } +} + +sub filter { + my $change = shift; + my $doc = $change->{doc} || next; + + _indexer->delete_by_term( field => '_id', term => $doc->{_id} ); + return 0 if $doc->{_deleted}; + + my $flat; + flatten( \$flat, $doc, '' ); + foreach my $field ( keys %$flat ) { + next if $schema->fetch_type($field); + $schema->spec_field( name => $field, type => $type ); + warn "# +++ $field\n"; + } + $flat->{doc} = $json->encode($doc); + warn "# add_doc ",dump($flat); + _indexer->add_doc($flat); + return 0; +} + +sub commit { + return unless $indexer; + $indexer->commit; + undef $indexer; + warn "# commit index done\n"; +} + +1;