repertoire-faceting 0.7.2 → 0.7.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5d3aadcd08762f8d0eb82f1b0a0a91f5866836f2
4
- data.tar.gz: 3261f97f7058932bd1e0a1a424b2c869903b8c8f
3
+ metadata.gz: 9f33047f3e67ebe050a869ec98d8f90042c284de
4
+ data.tar.gz: 66d4aa841d23714ede620a6ea56f2947f107a97c
5
5
  SHA512:
6
- metadata.gz: 1a6e1019f798bc2356afab1e5a2eec80b8dd289591a27095da38e296f86ea5c771008fb367be4869bab1ef2c1c2d009052c5a5c1cab55c93a4811dc35da1f705
7
- data.tar.gz: d897d8d5aa4ba04e747d46b81a2fb1dc05451991f6c0222a8cdae1fefa1269cea2b8575c4b90b690c1543fbea5e014b18746d8fb0ed9bb3664b4bd7c9228a721
6
+ metadata.gz: 5d86d643b9c881419b6dfe4f3608a56160193775057fc4f645f4b4367f10416cc5968e7f85a0f8059acc9a6c0f5cdc7ebf01a571d1bdc74e6661aa253f6a15e3
7
+ data.tar.gz: 890a6a07e726450554e058065f0e1fd2471805b8b4e6a751d87d564b5bc3dee58567176b56e8c445fc3df7f12009a9882d7eb2f854966d7facd0c82dce447675
data/FAQ CHANGED
@@ -95,6 +95,7 @@
95
95
 
96
96
  *A* See the syntax for defining jquery plugins - you can alter the defaults for all widgets by reassigning them in your view code.
97
97
 
98
+
98
99
  *Q* How do I make one-time, minor changes to the behaviour of a widget? For example, I want to add a control.
99
100
 
100
101
  *A* Use the inject option, which is part of the base functionality. Your injector function receives a reference to the widget's jquery element and to the widget javascript object. Use jquery to add your control's markup, then register an event handler to add its behaviour. For example, this injector adds a clear-all button in the title:
@@ -106,7 +107,7 @@
106
107
  handlers: {
107
108
  'click!.clear_control' : function(self) {
108
109
  self.refinements('genre').length = 0;
109
- self.state_changed();
110
+ self.trigger('changed');
110
111
  return false;
111
112
  }
112
113
  }
@@ -127,7 +128,7 @@ In injectors and handlers, you have access to the complete faceting widget API (
127
128
  $('#genre').facet({
128
129
  injectors: { ... },
129
130
  handlers: { ... },
130
- pre_update: function(state) { state.minimum = genre_min; }
131
+ state: function(state) { state.minimum = genre_min; }
131
132
  }
132
133
 
133
134
 
data/ext/Makefile CHANGED
@@ -8,7 +8,7 @@
8
8
  #
9
9
  #-------------------------------------------------------------------------
10
10
 
11
- API_VERSION = 0.7.2
11
+ API_VERSION = 0.7.3
12
12
 
13
13
  MODULES = signature/signature
14
14
  EXTENSION = signature/faceting \
@@ -2,5 +2,5 @@
2
2
 
3
3
  comment = 'API for faceted indexing and queries (based on plv8 + bytea bitmaps)'
4
4
  requires = 'plv8, plpgsql'
5
- default_version = '0.7.2'
5
+ default_version = '0.7.3'
6
6
  schema = 'facet'
@@ -0,0 +1,251 @@
1
+ -- ============================================================================
2
+ -- Faceting API implementing bitmap indices using a custom C datatype and
3
+ -- associated functions.
4
+ --
5
+ -- This API is to be preferred in all situations where it is possible to
6
+ -- build and install the datatype (requires superuser access to PostgreSQL)
7
+ --
8
+ -- Christopher York
9
+ -- MIT Hyperstudio
10
+ -- February 2014
11
+ -- ============================================================================
12
+
13
+ -- complain if script is sourced in psql, rather than via CREATE EXTENSION
14
+ \echo Use "CREATE EXTENSION faceting" to load this the default faceting API.\quit
15
+
16
+ -- functions for bitmap indices using datatype written in C
17
+
18
+ CREATE TYPE @extschema@.signature;
19
+
20
+ -- basic i/o functions for signatures
21
+
22
+ CREATE FUNCTION @extschema@.sig_in( cstring )
23
+ RETURNS signature
24
+ AS 'signature.so', 'sig_in'
25
+ LANGUAGE C STRICT;
26
+
27
+ CREATE FUNCTION @extschema@.sig_out( signature )
28
+ RETURNS cstring
29
+ AS 'signature.so', 'sig_out'
30
+ LANGUAGE C STRICT;
31
+
32
+ -- signature postgresql type
33
+
34
+ CREATE TYPE @extschema@.signature (
35
+ INTERNALLENGTH = VARIABLE,
36
+ INPUT = sig_in,
37
+ OUTPUT = sig_out,
38
+ STORAGE = extended
39
+ );
40
+
41
+ -- functions for signatures
42
+
43
+ CREATE FUNCTION @extschema@.sig_resize( signature, INT )
44
+ RETURNS signature
45
+ AS 'signature.so', 'sig_resize'
46
+ LANGUAGE C STRICT IMMUTABLE;
47
+
48
+ CREATE FUNCTION @extschema@.sig_set( signature, INT, INT )
49
+ RETURNS signature
50
+ AS 'signature.so', 'sig_set'
51
+ LANGUAGE C STRICT IMMUTABLE;
52
+
53
+ CREATE FUNCTION @extschema@.sig_set( signature, INT )
54
+ RETURNS signature
55
+ AS 'signature.so', 'sig_set'
56
+ LANGUAGE C STRICT IMMUTABLE;
57
+
58
+ CREATE FUNCTION @extschema@.sig_get( signature, INT )
59
+ RETURNS INT
60
+ AS 'signature.so', 'sig_get'
61
+ LANGUAGE C STRICT IMMUTABLE;
62
+
63
+ CREATE FUNCTION @extschema@.sig_length( signature )
64
+ RETURNS INT
65
+ AS 'signature.so', 'sig_length'
66
+ LANGUAGE C STRICT IMMUTABLE;
67
+
68
+ CREATE FUNCTION @extschema@.sig_min( signature )
69
+ RETURNS INT
70
+ AS 'signature.so', 'sig_min'
71
+ LANGUAGE C STRICT IMMUTABLE;
72
+
73
+ CREATE FUNCTION @extschema@.sig_and( signature, signature )
74
+ RETURNS signature
75
+ AS 'signature.so', 'sig_and'
76
+ LANGUAGE C STRICT IMMUTABLE;
77
+
78
+ CREATE FUNCTION @extschema@.sig_or( signature, signature )
79
+ RETURNS signature
80
+ AS 'signature.so', 'sig_or'
81
+ LANGUAGE C STRICT IMMUTABLE;
82
+
83
+ CREATE FUNCTION @extschema@.sig_xor( signature )
84
+ RETURNS signature
85
+ AS 'signature.so', 'sig_xor'
86
+ LANGUAGE C STRICT IMMUTABLE;
87
+
88
+ CREATE FUNCTION @extschema@.count( signature )
89
+ RETURNS INT
90
+ AS 'signature.so', 'count'
91
+ LANGUAGE C STRICT IMMUTABLE;
92
+
93
+ CREATE FUNCTION @extschema@.contains( signature, INT )
94
+ RETURNS BOOL
95
+ AS 'signature.so', 'contains'
96
+ LANGUAGE C STRICT IMMUTABLE;
97
+
98
+ CREATE FUNCTION @extschema@.members( signature )
99
+ RETURNS SETOF INT
100
+ AS 'signature.so', 'members'
101
+ LANGUAGE C STRICT IMMUTABLE;
102
+
103
+ CREATE FUNCTION @extschema@.sig_cmp( signature, signature )
104
+ RETURNS INT
105
+ AS 'signature.so', 'sig_cmp'
106
+ LANGUAGE C STRICT IMMUTABLE;
107
+
108
+ CREATE FUNCTION @extschema@.sig_lt( signature, signature )
109
+ RETURNS BOOL
110
+ AS 'signature.so', 'sig_lt'
111
+ LANGUAGE C STRICT IMMUTABLE;
112
+
113
+ CREATE FUNCTION @extschema@.sig_lte( signature, signature )
114
+ RETURNS BOOL
115
+ AS 'signature.so', 'sig_lte'
116
+ LANGUAGE C STRICT IMMUTABLE;
117
+
118
+ CREATE FUNCTION @extschema@.sig_eq( signature, signature )
119
+ RETURNS BOOL
120
+ AS 'signature.so', 'sig_eq'
121
+ LANGUAGE C STRICT IMMUTABLE;
122
+
123
+ CREATE FUNCTION @extschema@.sig_gt( signature, signature )
124
+ RETURNS BOOL
125
+ AS 'signature.so', 'sig_gt'
126
+ LANGUAGE C STRICT IMMUTABLE;
127
+
128
+ CREATE FUNCTION @extschema@.sig_gte( signature, signature )
129
+ RETURNS BOOL
130
+ AS 'signature.so', 'sig_gte'
131
+ LANGUAGE C STRICT IMMUTABLE;
132
+
133
+
134
+ -- operators for signatures
135
+
136
+ CREATE OPERATOR @extschema@.& (
137
+ leftarg = signature,
138
+ rightarg = signature,
139
+ procedure = @extschema@.sig_and,
140
+ commutator = &
141
+ );
142
+
143
+ CREATE OPERATOR @extschema@.| (
144
+ leftarg = signature,
145
+ rightarg = signature,
146
+ procedure = @extschema@.sig_or,
147
+ commutator = |
148
+ );
149
+
150
+ CREATE OPERATOR @extschema@.+ (
151
+ leftarg = signature,
152
+ rightarg = int,
153
+ procedure = @extschema@.sig_set
154
+ );
155
+
156
+ CREATE OPERATOR @extschema@.< (
157
+ leftarg = signature, rightarg = signature, procedure = sig_lt,
158
+ commutator = > , negator = >= ,
159
+ restrict = scalarltsel, join = scalarltjoinsel
160
+ );
161
+
162
+ CREATE OPERATOR @extschema@.<= (
163
+ leftarg = signature, rightarg = signature, procedure = sig_lte,
164
+ commutator = >= , negator = > ,
165
+ restrict = scalarltsel, join = scalarltjoinsel
166
+ );
167
+
168
+ CREATE OPERATOR @extschema@.= (
169
+ leftarg = signature, rightarg = signature, procedure = sig_eq,
170
+ commutator = = , negator = <> ,
171
+ restrict = eqsel, join = eqjoinsel
172
+ );
173
+
174
+ CREATE OPERATOR >= (
175
+ leftarg = signature, rightarg = signature, procedure = sig_gte,
176
+ commutator = <= , negator = < ,
177
+ restrict = scalargtsel, join = scalargtjoinsel
178
+ );
179
+
180
+ CREATE OPERATOR @extschema@.> (
181
+ leftarg = signature, rightarg = signature, procedure = sig_gt,
182
+ commutator = < , negator = <= ,
183
+ restrict = scalargtsel, join = scalargtjoinsel
184
+ );
185
+
186
+ -- index operator classes for signatures
187
+
188
+ CREATE OPERATOR CLASS @extschema@.signature_ops
189
+ DEFAULT FOR TYPE signature USING btree AS
190
+ OPERATOR 1 < ,
191
+ OPERATOR 2 <= ,
192
+ OPERATOR 3 = ,
193
+ OPERATOR 4 >= ,
194
+ OPERATOR 5 > ,
195
+ FUNCTION 1 sig_cmp(signature, signature);
196
+
197
+
198
+ -- aggregate functions for faceting
199
+
200
+ CREATE AGGREGATE @extschema@.collect( signature )
201
+ (
202
+ sfunc = @extschema@.sig_or,
203
+ stype = signature
204
+ );
205
+
206
+ CREATE AGGREGATE @extschema@.filter( signature )
207
+ (
208
+ sfunc = @extschema@.sig_and,
209
+ stype = signature
210
+ );
211
+
212
+ CREATE AGGREGATE @extschema@.signature( INT )
213
+ (
214
+ sfunc = @extschema@.sig_set,
215
+ stype = signature,
216
+ initcond = '0'
217
+ );-- ============================================================================
218
+ -- These functions are common to all bindings of the Repertoire faceting API
219
+ --
220
+ -- Christopher York
221
+ -- MIT Hyperstudio
222
+ -- February 2014
223
+ -- ============================================================================
224
+
225
+
226
+ -- Aggregator to measure how many bits from a loosely-packed id column would be wasted, if
227
+ -- they were all collected into a bitset signature. Returns a float between 0 (no waste)
228
+ -- and 1.0 (all waste). An example of its use:
229
+ --
230
+ -- SELECT wastage(id) FROM nobelists
231
+ -- =# 0.999999
232
+ --
233
+ -- ALTER TABLE nobelists ADD COLUMN _packed_id SERIAL
234
+ -- SELECT wastage(_packed_id) FROM nobelists
235
+ -- =# 0.015625
236
+ --
237
+ CREATE FUNCTION @extschema@.wastage_proportion( state INT[] ) RETURNS double precision AS $$
238
+ SELECT (1.0 - (state[1]::double precision / (COALESCE(state[2], 0.0) + 1.0)))
239
+ $$ LANGUAGE sql;
240
+
241
+ CREATE FUNCTION @extschema@.wastage_accum( state INT[], val INT ) RETURNS INT[] AS $$
242
+ SELECT ARRAY[ state[1]+1, GREATEST(state[2], val) ];
243
+ $$ LANGUAGE sql;
244
+
245
+ CREATE AGGREGATE @extschema@.wastage( INT )
246
+ (
247
+ sfunc = @extschema@.wastage_accum,
248
+ stype = INT[],
249
+ finalfunc = @extschema@.wastage_proportion,
250
+ initcond = '{0,0}'
251
+ );
@@ -0,0 +1,207 @@
1
+ -- ============================================================================
2
+ -- Faceting API implementing bitmap indices using PostgreSQL's built-in BYTEA
3
+ -- type, processed using plv8 typed arrays.
4
+ --
5
+ -- This API is suitable for deployment on Heroku, where plv8 is installed by
6
+ -- default. Performance is many times better than the VARBIT-based faceting
7
+ -- API, primarily because of optimisations in memory handling in the count
8
+ -- function.
9
+ --
10
+ -- See https://code.google.com/p/plv8js/wiki/PLV8
11
+ -- https://postgres.heroku.com/blog/past/2013/6/5/javascript_in_your_postgres/
12
+ --
13
+ -- Christopher York
14
+ -- MIT Hyperstudio
15
+ -- February 2014
16
+ -- ============================================================================
17
+
18
+ CREATE EXTENSION IF NOT EXISTS plv8;
19
+
20
+ SET bytea_output TO hex;
21
+
22
+ -- these functions are in pl/pgsql, because they involve appending bytea values,
23
+ -- which is easier done with direct access to the || operator
24
+
25
+ CREATE FUNCTION @extschema@.sig_resize( sig BYTEA, bits INT ) RETURNS BYTEA AS $$
26
+ DECLARE
27
+ len INT;
28
+ bytes INT;
29
+ BEGIN
30
+ bytes := ceil(bits / 8.0)::INT;
31
+ len := length(sig);
32
+ IF bytes > len THEN
33
+ -- RAISE NOTICE 'Extending signature from % to % bytes', len, bytes;
34
+ RETURN sig || ('\x' || repeat('00', bytes - len))::BYTEA;
35
+ ELSIF bits < len THEN
36
+ -- no provision in PostgreSQL for truncating a BYTEA
37
+ RETURN sig;
38
+ END IF;
39
+ RETURN sig;
40
+ END $$ LANGUAGE plpgsql STRICT IMMUTABLE;
41
+
42
+ CREATE FUNCTION @extschema@.sig_set( sig BYTEA, pos INT, val INT ) RETURNS BYTEA AS $$
43
+ BEGIN
44
+ RETURN set_bit(@extschema@.sig_resize(sig, pos+1), pos, val);
45
+ END $$ LANGUAGE plpgsql STRICT IMMUTABLE;
46
+
47
+ CREATE FUNCTION @extschema@.sig_set( sig BYTEA, pos INT ) RETURNS BYTEA AS $$
48
+ BEGIN
49
+ RETURN @extschema@.sig_set(sig, pos, 1);
50
+ END $$ LANGUAGE plpgsql STRICT IMMUTABLE;
51
+
52
+ -- these functions are in javascript, because (1) pl/pgsql is close
53
+ -- to the worst language in the world; (2) plv8's typed arrays make
54
+ -- the count function much faster
55
+
56
+ CREATE FUNCTION @extschema@.sig_get( sig BYTEA, pos INT ) RETURNS INT AS $$
57
+ if (pos <= sig.length * 8) {
58
+ return sig[ Math.floor(pos / 8) ] >> (pos % 8) & 1;
59
+ } else {
60
+ return 0;
61
+ }
62
+ $$ LANGUAGE plv8 STRICT IMMUTABLE;
63
+
64
+ CREATE FUNCTION @extschema@.sig_length( sig BYTEA ) RETURNS INT AS $$
65
+ return sig.length;
66
+ $$ LANGUAGE plv8 STRICT IMMUTABLE;
67
+
68
+ CREATE FUNCTION @extschema@.sig_and(sig1 BYTEA, sig2 BYTEA) RETURNS BYTEA AS $$
69
+ if (sig2.length < sig1.length) {
70
+ var tmp = sig1;
71
+ sig1 = sig2;
72
+ sig2 = tmp;
73
+ }
74
+ for (var i = 0; i < sig1.length; i++) {
75
+ sig1[i] = sig1[i] & sig2[i];
76
+ }
77
+ return sig1;
78
+ $$ LANGUAGE plv8 STRICT IMMUTABLE;
79
+
80
+ CREATE FUNCTION @extschema@.sig_or(sig1 BYTEA, sig2 BYTEA) RETURNS BYTEA AS $$
81
+ if (sig2.length > sig1.length) {
82
+ var tmp = sig1;
83
+ sig1 = sig2;
84
+ sig2 = tmp;
85
+ }
86
+ for (var i = 0; i < sig2.length; i++) {
87
+ sig1[i] = sig1[i] | sig2[i];
88
+ }
89
+ return sig1;
90
+ $$ LANGUAGE plv8 STRICT IMMUTABLE;
91
+
92
+ CREATE FUNCTION @extschema@.count( sig BYTEA ) RETURNS int4 AS $$
93
+ var count_table = [
94
+ 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
95
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
96
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
97
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
98
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
99
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
100
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
101
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
102
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
103
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
104
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
105
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
106
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
107
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
108
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
109
+ 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8,
110
+ ];
111
+ var count = 0;
112
+ for (var i = 0; i < sig.length; i++) { count += count_table[ sig[i] ]; }
113
+ return count;
114
+ $$ LANGUAGE plv8 STRICT IMMUTABLE;
115
+
116
+ CREATE FUNCTION @extschema@.contains( sig BYTEA, pos INT ) RETURNS BOOL AS $$
117
+ return (pos <= sig.length * 8) && (sig[ Math.floor(pos / 8) ] >> (pos % 8) & 1);
118
+ $$ LANGUAGE plv8 STRICT IMMUTABLE;
119
+
120
+ CREATE FUNCTION @extschema@.members( sig BYTEA ) RETURNS SETOF INT AS $$
121
+ for (var i = 0; i < sig.length; i++) {
122
+ for (var j = 0; j < 8; j++) {
123
+ if (sig[i] >> j & 1) {
124
+ plv8.return_next(i * 8 + j);
125
+ }
126
+ }
127
+ }
128
+ $$ LANGUAGE plv8 STRICT IMMUTABLE;
129
+
130
+
131
+ -- operators for faceting
132
+
133
+ CREATE OPERATOR @extschema@.& (
134
+ leftarg = BYTEA,
135
+ rightarg = BYTEA,
136
+ procedure = @extschema@.sig_and,
137
+ commutator = &
138
+ );
139
+
140
+ CREATE OPERATOR @extschema@.| (
141
+ leftarg = BYTEA,
142
+ rightarg = BYTEA,
143
+ procedure = @extschema@.sig_or,
144
+ commutator = |
145
+ );
146
+
147
+ CREATE OPERATOR @extschema@.+ (
148
+ leftarg = BYTEA,
149
+ rightarg = int,
150
+ procedure = @extschema@.sig_set
151
+ );
152
+
153
+
154
+ -- aggregate functions for faceting
155
+
156
+ CREATE AGGREGATE @extschema@.collect( BYTEA )
157
+ (
158
+ sfunc = @extschema@.sig_or,
159
+ stype = BYTEA
160
+ );
161
+
162
+ CREATE AGGREGATE @extschema@.filter( BYTEA )
163
+ (
164
+ sfunc = @extschema@.sig_and,
165
+ stype = BYTEA
166
+ );
167
+
168
+ CREATE AGGREGATE @extschema@.signature( INT )
169
+ (
170
+ sfunc = @extschema@.sig_set,
171
+ stype = BYTEA,
172
+ initcond = ''
173
+ );-- ============================================================================
174
+ -- These functions are common to all bindings of the Repertoire faceting API
175
+ --
176
+ -- Christopher York
177
+ -- MIT Hyperstudio
178
+ -- February 2014
179
+ -- ============================================================================
180
+
181
+
182
+ -- Aggregator to measure how many bits from a loosely-packed id column would be wasted, if
183
+ -- they were all collected into a bitset signature. Returns a float between 0 (no waste)
184
+ -- and 1.0 (all waste). An example of its use:
185
+ --
186
+ -- SELECT wastage(id) FROM nobelists
187
+ -- =# 0.999999
188
+ --
189
+ -- ALTER TABLE nobelists ADD COLUMN _packed_id SERIAL
190
+ -- SELECT wastage(_packed_id) FROM nobelists
191
+ -- =# 0.015625
192
+ --
193
+ CREATE FUNCTION @extschema@.wastage_proportion( state INT[] ) RETURNS double precision AS $$
194
+ SELECT (1.0 - (state[1]::double precision / (COALESCE(state[2], 0.0) + 1.0)))
195
+ $$ LANGUAGE sql;
196
+
197
+ CREATE FUNCTION @extschema@.wastage_accum( state INT[], val INT ) RETURNS INT[] AS $$
198
+ SELECT ARRAY[ state[1]+1, GREATEST(state[2], val) ];
199
+ $$ LANGUAGE sql;
200
+
201
+ CREATE AGGREGATE @extschema@.wastage( INT )
202
+ (
203
+ sfunc = @extschema@.wastage_accum,
204
+ stype = INT[],
205
+ finalfunc = @extschema@.wastage_proportion,
206
+ initcond = '{0,0}'
207
+ );
@@ -0,0 +1,198 @@
1
+ -- ============================================================================
2
+ -- Faceting API implementing bitmap indices using PostgreSQL's built-in VARBIT
3
+ -- type, processed using the built-in language pl/pgsql.
4
+ --
5
+ -- This API is suitable for deployment on any host, since it requires no
6
+ -- PostgreSQL extensions outside the default install.
7
+ --
8
+ -- However, performance is limited to around 30,000 items in practice (in part
9
+ -- because of unnecessary duplication of varbit values when pl/pgsql evaluates
10
+ -- the count function.)
11
+ --
12
+ -- The 'signature' C-based faceting API is preferable for any install where
13
+ -- you have superuser access to the database.
14
+ --
15
+ -- Christopher York
16
+ -- MIT Hyperstudio
17
+ -- February 2014
18
+ -- ============================================================================
19
+
20
+ CREATE FUNCTION @extschema@.sig_resize( sig VARBIT, bits INT ) RETURNS VARBIT AS $$
21
+ DECLARE
22
+ len INT;
23
+ BEGIN
24
+ len := length(sig);
25
+ IF bits > len THEN
26
+ RETURN sig || repeat('0', bits - len)::VARBIT;
27
+ ELSIF bits < len THEN
28
+ RETURN substring(sig FROM 1 FOR bits);
29
+ END IF;
30
+ RETURN sig;
31
+ END $$ LANGUAGE plpgsql STRICT IMMUTABLE;
32
+
33
+ CREATE FUNCTION @extschema@.sig_set( sig VARBIT, pos INT, val INT ) RETURNS VARBIT AS $$
34
+ DECLARE
35
+ len INT;
36
+ BEGIN
37
+ len := length(sig);
38
+ IF pos >= len THEN
39
+ IF val > 0 THEN
40
+ RETURN set_bit(@extschema@.sig_resize(sig, pos+1), pos, 1);
41
+ ELSE
42
+ RETURN sig;
43
+ END IF;
44
+ ELSE
45
+ RETURN set_bit(sig, pos, val);
46
+ END IF;
47
+ END $$ LANGUAGE plpgsql STRICT IMMUTABLE;
48
+
49
+ CREATE FUNCTION @extschema@.sig_set( sig VARBIT, pos INT ) RETURNS VARBIT AS $$
50
+ BEGIN
51
+ RETURN @extschema@.sig_set(sig, pos, 1);
52
+ END $$ LANGUAGE plpgsql STRICT IMMUTABLE;
53
+
54
+ CREATE FUNCTION @extschema@.sig_get( sig VARBIT, pos INT ) RETURNS INT AS $$
55
+ DECLARE
56
+ len INT;
57
+ BEGIN
58
+ len := length(sig);
59
+ IF pos >= len THEN
60
+ RETURN 0;
61
+ ELSE
62
+ RETURN get_bit(sig, pos);
63
+ END IF;
64
+ END $$ LANGUAGE plpgsql STRICT IMMUTABLE;
65
+
66
+ CREATE FUNCTION @extschema@.sig_length( sig VARBIT ) RETURNS INT AS $$
67
+ BEGIN
68
+ RETURN length(sig);
69
+ END $$ LANGUAGE plpgsql STRICT IMMUTABLE;
70
+
71
+ CREATE FUNCTION @extschema@.sig_min( sig VARBIT ) RETURNS INT AS $$
72
+ BEGIN
73
+ RETURN position('1' in sig) - 1;
74
+ END $$ LANGUAGE plpgsql STRICT IMMUTABLE;
75
+
76
+ CREATE FUNCTION @extschema@.sig_and( sig1 VARBIT, sig2 VARBIT ) RETURNS VARBIT AS $$
77
+ DECLARE
78
+ len INT;
79
+ BEGIN
80
+ len := GREATEST(length(sig1), length(sig2));
81
+ RETURN bitand(@extschema@.sig_resize(sig1, len), @extschema@.sig_resize(sig2, len)) ;
82
+ END $$ LANGUAGE plpgsql STRICT IMMUTABLE;
83
+
84
+ CREATE FUNCTION @extschema@.sig_or( sig1 VARBIT, sig2 VARBIT ) RETURNS VARBIT AS $$
85
+ DECLARE
86
+ len INT;
87
+ BEGIN
88
+ len := GREATEST(length(sig1), length(sig2));
89
+ RETURN bitor(@extschema@.sig_resize(sig1, len), @extschema@.sig_resize(sig2, len)) ;
90
+ END $$ LANGUAGE plpgsql STRICT IMMUTABLE;
91
+
92
+ CREATE FUNCTION @extschema@.sig_xor( sig1 VARBIT, sig2 VARBIT ) RETURNS VARBIT AS $$
93
+ DECLARE
94
+ len INT;
95
+ BEGIN
96
+ len := GREATEST(length(sig1), length(sig2));
97
+ RETURN bitxor(@extschema@.sig_resize(sig1, len), @extschema@.sig_resize(sig2, len)) ;
98
+ END $$ LANGUAGE plpgsql STRICT IMMUTABLE;
99
+
100
+ CREATE FUNCTION @extschema@.count( sig VARBIT ) RETURNS INT AS $$
101
+ BEGIN
102
+ -- This is, by any measure, horrific. However, it appears to be the only
103
+ -- way to use PostgreSQL built in functions to count bits in a bit string.
104
+ RETURN length(replace(sig::TEXT, '0', ''));
105
+ END $$ LANGUAGE plpgsql STRICT IMMUTABLE;
106
+
107
+ CREATE FUNCTION @extschema@.contains( sig VARBIT, pos INT ) RETURNS BOOL AS $$
108
+ BEGIN
109
+ RETURN @extschema@.sig_get(sig, pos) = 1;
110
+ END $$ LANGUAGE plpgsql STRICT IMMUTABLE;
111
+
112
+ CREATE FUNCTION @extschema@.members( sig VARBIT ) RETURNS SETOF INT AS $$
113
+ BEGIN
114
+ FOR i IN 0 .. length(sig) - 1 LOOP
115
+ IF @extschema@.contains(sig, i) THEN
116
+ RETURN NEXT i;
117
+ END IF;
118
+ END LOOP;
119
+ END $$ LANGUAGE plpgsql STRICT IMMUTABLE;
120
+
121
+
122
+ -- operators for faceting
123
+
124
+ CREATE OPERATOR @extschema@.& (
125
+ leftarg = VARBIT,
126
+ rightarg = VARBIT,
127
+ procedure = @extschema@.sig_and,
128
+ commutator = &
129
+ );
130
+
131
+ CREATE OPERATOR @extschema@.| (
132
+ leftarg = VARBIT,
133
+ rightarg = VARBIT,
134
+ procedure = @extschema@.sig_or,
135
+ commutator = |
136
+ );
137
+
138
+ CREATE OPERATOR @extschema@.+ (
139
+ leftarg = VARBIT,
140
+ rightarg = int,
141
+ procedure = @extschema@.sig_set
142
+ );
143
+
144
+
145
+ -- aggregate functions for faceting
146
+
147
+ CREATE AGGREGATE @extschema@.collect( VARBIT )
148
+ (
149
+ sfunc = @extschema@.sig_or,
150
+ stype = VARBIT
151
+ );
152
+
153
+ CREATE AGGREGATE @extschema@.filter( VARBIT )
154
+ (
155
+ sfunc = @extschema@.sig_and,
156
+ stype = VARBIT
157
+ );
158
+
159
+ CREATE AGGREGATE @extschema@.signature( INT )
160
+ (
161
+ sfunc = @extschema@.sig_set,
162
+ stype = VARBIT,
163
+ initcond = '0'
164
+ );-- ============================================================================
165
+ -- These functions are common to all bindings of the Repertoire faceting API
166
+ --
167
+ -- Christopher York
168
+ -- MIT Hyperstudio
169
+ -- February 2014
170
+ -- ============================================================================
171
+
172
+
173
+ -- Aggregator to measure how many bits from a loosely-packed id column would be wasted, if
174
+ -- they were all collected into a bitset signature. Returns a float between 0 (no waste)
175
+ -- and 1.0 (all waste). An example of its use:
176
+ --
177
+ -- SELECT wastage(id) FROM nobelists
178
+ -- =# 0.999999
179
+ --
180
+ -- ALTER TABLE nobelists ADD COLUMN _packed_id SERIAL
181
+ -- SELECT wastage(_packed_id) FROM nobelists
182
+ -- =# 0.015625
183
+ --
184
+ CREATE FUNCTION @extschema@.wastage_proportion( state INT[] ) RETURNS double precision AS $$
185
+ SELECT (1.0 - (state[1]::double precision / (COALESCE(state[2], 0.0) + 1.0)))
186
+ $$ LANGUAGE sql;
187
+
188
+ CREATE FUNCTION @extschema@.wastage_accum( state INT[], val INT ) RETURNS INT[] AS $$
189
+ SELECT ARRAY[ state[1]+1, GREATEST(state[2], val) ];
190
+ $$ LANGUAGE sql;
191
+
192
+ CREATE AGGREGATE @extschema@.wastage( INT )
193
+ (
194
+ sfunc = @extschema@.wastage_accum,
195
+ stype = INT[],
196
+ finalfunc = @extschema@.wastage_proportion,
197
+ initcond = '{0,0}'
198
+ );
@@ -2,5 +2,5 @@
2
2
 
3
3
  comment = 'API for faceted indexing and queries (based on custom C bitmap type)'
4
4
  requires = plpgsql
5
- default_version = '0.7.2'
5
+ default_version = '0.7.3'
6
6
  schema = 'facet'
Binary file
Binary file
@@ -3,5 +3,5 @@
3
3
  comment = 'API for faceted indexing and queries (based on builtin VARBIT bit strings)'
4
4
  requires = plpgsql
5
5
  superuser = false
6
- default_version = '0.7.2'
6
+ default_version = '0.7.3'
7
7
  schema = 'facet'
@@ -74,28 +74,6 @@ repertoire.facet_context = function(context_name, state_fn, options) {
74
74
  filter[facet] = filter[facet] || [];
75
75
  }
76
76
 
77
- //
78
- // Calculate facet value counts from webservice
79
- //
80
- // By default, the url is '/<context>/counts/<facet>'
81
- //
82
- self.counts = function(facet_name, callback, $elem) {
83
- var url = self.facet_url('counts', facet_name);
84
- // package up the faceting state and send back to results rendering service
85
- self.fetch(self.params(), url, 'json', callback, $elem);
86
- };
87
-
88
- //
89
- // Update query results from webservice
90
- //
91
- // By default, the url is '/<context>/counts/<facet>'
92
- //
93
- self.results = function(type, callback, $elem) {
94
- var url = self.facet_url('results');
95
- // package up the faceting state and send back to results rendering service
96
- self.fetch(self.params(), url, type, callback, $elem);
97
- };
98
-
99
77
  //
100
78
  // Convenience function for constructing faceting urls
101
79
  //
@@ -46,13 +46,24 @@ repertoire.facet = function($facet, options) {
46
46
  // do not bubble event
47
47
  return false;
48
48
  });
49
+
50
+ //
51
+ // Calculate facet value counts from webservice
52
+ //
53
+ // By default, the url is '/<context>/counts/<facet>'
54
+ //
55
+ self.fetch = function(facet_name, callback, $elem) {
56
+ var context = self.context();
57
+ var url = context.facet_url('counts', facet_name);
58
+ // package up the faceting state and send back to results rendering service
59
+ context.fetch(self.params(), url, 'json', callback, $elem);
60
+ };
49
61
 
50
62
  //
51
63
  // Ajax callback for facet value counts
52
64
  //
53
65
  self.reload = function(callback) {
54
- var context = self.context();
55
- context.counts(self.facet_name(), callback, $facet);
66
+ self.fetch(self.facet_name(), callback, $facet);
56
67
  }
57
68
 
58
69
  //
@@ -58,6 +58,22 @@ repertoire.facet_widget = function($widget, options) {
58
58
  return options.context || self.context().name();
59
59
  };
60
60
 
61
+ self.state = function() {
62
+ if (options.state)
63
+ return options.state();
64
+ else return {};
65
+ }
66
+
67
+ //
68
+ // Return any extra params to send to the web-server.
69
+ //
70
+ // By default, params encompass all context state, plus any widget state.
71
+ //
72
+ self.params = function() {
73
+ var context = self.context();
74
+ return $.extend({}, context.params(), self.state());
75
+ };
76
+
61
77
  //
62
78
  // Capitalize and return a string
63
79
  //
@@ -25,14 +25,24 @@
25
25
  repertoire.results = function($results, options) {
26
26
  // inherit basic facet widget behaviour
27
27
  var self = repertoire.facet_widget($results, options);
28
-
28
+
29
+ //
30
+ // Update query results from webservice
31
+ //
32
+ // By default, the url is '/<context>/counts/<facet>'
33
+ //
34
+ self.fetch = function(type, callback, $elem) {
35
+ var context = self.context();
36
+ var url = context.facet_url('results');
37
+ // package up the faceting state and send back to results rendering service
38
+ context.fetch(self.params(), url, type, callback, $elem);
39
+ };
29
40
 
30
41
  //
31
42
  // Ajax callback for results
32
43
  //
33
44
  self.reload = function(callback) {
34
- var context = self.context();
35
- context.results(options.type, callback, $results);
45
+ self.fetch(options.type, callback, $results);
36
46
  }
37
47
 
38
48
  //
@@ -70,7 +70,7 @@ repertoire.model = function(options) {
70
70
  if ($elem)
71
71
  $elem.addClass(spinnerClass);
72
72
 
73
- $.ajax({
73
+ return $.ajax({
74
74
  async: async,
75
75
  url: url,
76
76
  data: self.to_query_string(params),
@@ -80,8 +80,9 @@ repertoire.model = function(options) {
80
80
  /* beforeSend: function(xhr) { xhr.setRequestHeader("Accept", "application/json") } */
81
81
  success: callback,
82
82
  error: function(request, textStatus, errorThrown) {
83
- if ($elem)
84
- $elem.html(options.error || 'Could not load');
83
+ if ($elem) {
84
+ $elem.html(options.error || 'Could not load');
85
+ };
85
86
  },
86
87
  complete: function () {
87
88
  if ($elem)
@@ -24,7 +24,7 @@
24
24
  // error - text to display if ajax load fails
25
25
  // injectors - additional jquery markup to inject into widget (see FAQ)
26
26
  // handlers - additional jquery event handlers to add to widget (see FAQ)
27
- // state - additional pre-processing for params sent to webservice (see FAQ)
27
+ // state - additional pre-processing for params sent to webservice (see FAQ)
28
28
  //
29
29
  // Sub-classes are required to over-ride one method: self.render(). If you wish to
30
30
  // use a data model, store a subclass of rep.wigets/model in your widget.
@@ -32,11 +32,11 @@
32
32
  repertoire.widget = function(selector, options) {
33
33
  // this object is an abstract class
34
34
  var self = {};
35
-
35
+
36
36
  // private variables
37
37
  var $widget = $(selector);
38
38
  options = options || {};
39
-
39
+
40
40
  // mix in event handling functionality
41
41
  repertoire.events(self, $widget);
42
42
 
@@ -47,7 +47,9 @@ repertoire.widget = function(selector, options) {
47
47
  register_handlers(options.handlers);
48
48
 
49
49
  // load once at beginning
50
- self.refresh();
50
+ if (! options.delay_loading) {
51
+ self.refresh();
52
+ };
51
53
  }
52
54
 
53
55
  //
@@ -64,21 +66,21 @@ repertoire.widget = function(selector, options) {
64
66
  self.refresh = function() {
65
67
  var callback,
66
68
  callbackStamp;
67
-
69
+
68
70
  // pass to custom state processor
69
71
  if (options.state !== undefined)
70
72
  options.state(self);
71
73
 
72
74
  // adjust timestamp to most recent ajax call
73
75
  ajaxStamp = callbackStamp = Date.now();
74
-
76
+
75
77
  callback = function() {
76
78
  // reject if this is an old ajax request
77
79
  if (callbackStamp < ajaxStamp) return;
78
80
 
79
81
  // render the widget
80
82
  var markup = self.render.apply(self, arguments);
81
-
83
+
82
84
  // inject any custom markup
83
85
  if (options.injectors !== undefined)
84
86
  // TODO. figure out how to send all args to injectors
@@ -105,7 +107,7 @@ repertoire.widget = function(selector, options) {
105
107
  // };
106
108
  //
107
109
  // (2) If you just want to tweak the superclass' view:
108
- //
110
+ //
109
111
  // var template_fn = self.render; // idiom to access super.render()
110
112
  // self.render = function() {
111
113
  // var markup = template_fn();
@@ -118,8 +120,8 @@ repertoire.widget = function(selector, options) {
118
120
  self.render = function() {
119
121
  return $('<div class="rep"/>'); // namespace for all other widget css is 'rep'
120
122
  };
121
-
122
-
123
+
124
+
123
125
  //
124
126
  // A hook for use when your widget must render the results of an ajax callback. Put
125
127
  // the ajax call in self.reload(). Its results will be passed to self.render().
@@ -155,7 +157,7 @@ repertoire.widget = function(selector, options) {
155
157
  event_selector = parse_event_selector(event_selector);
156
158
  var event = event_selector[0],
157
159
  selector = event_selector[1]; // why doesn't JS support array decomposition?!?
158
-
160
+
159
161
  // bind new handler
160
162
  $widget.bind(event, function (e) {
161
163
  var $el = $(e.target);
@@ -172,9 +174,9 @@ repertoire.widget = function(selector, options) {
172
174
  }
173
175
  });
174
176
  }
175
-
177
+
176
178
  // PRIVATE
177
-
179
+
178
180
  // register a collection of event handlers
179
181
  function register_handlers(handlers) {
180
182
  $.each(handlers, function(selector, handler) {
@@ -198,7 +200,7 @@ repertoire.widget = function(selector, options) {
198
200
  }
199
201
  });
200
202
  }
201
-
203
+
202
204
  // parse an event name and selector spec
203
205
  function parse_event_selector(event_selector) {
204
206
  var s = event_selector.split('!');
@@ -1,5 +1,5 @@
1
1
  module Repertoire
2
2
  module Faceting #:nodoc:
3
- VERSION = "0.7.2"
3
+ VERSION = "0.7.3"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: repertoire-faceting
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.2
4
+ version: 0.7.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Christopher York
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-05-28 00:00:00.000000000 Z
11
+ date: 2014-06-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -83,8 +83,13 @@ files:
83
83
  - ext/bytea/faceting_bytea.control
84
84
  - ext/common/util.sql
85
85
  - ext/extconf.rb
86
+ - ext/faceting--0.7.2.sql
87
+ - ext/faceting_bytea--0.7.2.sql
88
+ - ext/faceting_varbit--0.7.2.sql
86
89
  - ext/signature/faceting.control
87
90
  - ext/signature/signature.c
91
+ - ext/signature/signature.o
92
+ - ext/signature/signature.so
88
93
  - ext/signature/signature.sql
89
94
  - ext/varbit/faceting_varbit.control
90
95
  - ext/varbit/varbit.sql