Merge lp:~thisfred/u1db/ergo-sum into lp:u1db

Proposed by Eric Casteleijn on 2012-07-29
Status: Work in progress
Proposed branch: lp:~thisfred/u1db/ergo-sum
Merge into: lp:u1db
Prerequisite: lp:~thisfred/u1db/iterate-over-list-of-dicts
Diff against target: 228 lines (+85/-15)
5 files modified
html-docs/high-level-api.rst (+3/-0)
src/u1db_query.c (+40/-15)
u1db/query_parser.py (+21/-0)
u1db/tests/test_backends.py (+15/-0)
u1db/tests/test_query_parser.py (+6/-0)
To merge this branch: bzr merge lp:~thisfred/u1db/ergo-sum
Reviewer Review Type Date Requested Status
Ubuntu One hackers 2012-07-29 Pending
Review via email: mp+117186@code.launchpad.net

This proposal supersedes a proposal from 2012-07-29.

Commit Message

This adds a sum operator that acts like combine, except instead of indexing a bunch of string expressions, it indexes the sum of a bunch of integer expressions.

Description of the Change

This adds a sum operator that acts like combine, except instead of indexing a bunch of string expressions, it indexes the sum of a bunch of integer expressions.

To post a comment you must log in.
lp:~thisfred/u1db/ergo-sum updated on 2012-07-29
367. By Eric Casteleijn on 2012-07-29

updated api doc

Unmerged revisions

367. By Eric Casteleijn on 2012-07-29

updated api doc

366. By Eric Casteleijn on 2012-07-29

added sum operator

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'html-docs/high-level-api.rst'
2--- html-docs/high-level-api.rst 2012-07-26 13:34:58 +0000
3+++ html-docs/high-level-api.rst 2012-07-29 17:35:21 +0000
4@@ -231,6 +231,9 @@
5 field is absent, otherwise false
6 * ``combine(index_expression1, index_expression2, ...)`` - Combine the values
7 of an arbitrary number of sub expressions into a single index.
8+ * ``sum(index_expression1, index_expression2, ...)`` - Sum the values
9+ of an arbitrary number of (integer) sub expressions into a single index
10+ value.
11
12 So, the index expression ``splitwords(lower(field.name))`` applied to
13 a document with ID "doc3" and content::
14
15=== modified file 'src/u1db_query.c'
16--- src/u1db_query.c 2012-07-29 17:35:21 +0000
17+++ src/u1db_query.c 2012-07-29 17:35:21 +0000
18@@ -233,6 +233,7 @@
19 parse_tree *tree, json_object *obj, string_list *result);
20 static int op_bool(parse_tree *tree, json_object *obj, string_list *result);
21 static int op_combine(parse_tree *tree, json_object *obj, string_list *result);
22+static int op_sum(parse_tree *tree, json_object *obj, string_list *result);
23
24 static const int JUST_EXPRESSION[1] = {EXPRESSION};
25 static const int EXPRESSION_INTEGER[2] = {EXPRESSION, INTEGER};
26@@ -249,7 +250,8 @@
27 op_number, "number", json_type_int, 2, EXPRESSION_INTEGER,
28 op_split_words, "split_words", json_type_string, 1, JUST_EXPRESSION,
29 op_bool, "bool", json_type_boolean, 1, JUST_EXPRESSION,
30- op_combine, "combine", json_type_string, -1, JUST_EXPRESSION};
31+ op_combine, "combine", json_type_string, -1, JUST_EXPRESSION,
32+ op_sum, "sum", json_type_int, -1, JUST_EXPRESSION};
33
34 static int
35 extract_value(json_object *val, int value_type, string_list *values)
36@@ -368,14 +370,15 @@
37 }
38
39 static int
40-get_values(parse_tree *tree, json_object *obj, string_list *values)
41+get_values(parse_tree *tree, json_object *obj, int json_type,
42+ string_list *values)
43 {
44 int status = U1DB_OK;
45 if (tree->op) {
46 status = ((op_function)tree->op)(tree, obj, values);
47 } else {
48 status = extract_field_values(
49- obj, tree->field_path->head, json_type_string, values);
50+ obj, tree->field_path->head, json_type, values);
51 }
52 return status;
53 }
54@@ -392,7 +395,7 @@
55 status = init_list(&values);
56 if (status != U1DB_OK)
57 return status;
58- status = get_values(tree->first_child, obj, values);
59+ status = get_values(tree->first_child, obj, json_type_string, values);
60 if (status != U1DB_OK)
61 goto finish;
62 for (item = values->head; item != NULL; item = item->next)
63@@ -434,8 +437,7 @@
64 status = init_list(&values);
65 if (status != U1DB_OK)
66 return status;
67- status = extract_field_values(
68- obj, node->field_path->head, json_type_int, values);
69+ status = get_values(tree->first_child, obj, json_type_int, values);
70 if (status != U1DB_OK)
71 goto finish;
72 node = node->next_sibling;
73@@ -493,9 +495,8 @@
74 parse_tree *node = NULL;
75 int status = U1DB_OK;
76
77- node = tree->first_child;
78 for (node = tree->first_child; node != NULL; node = node->next_sibling) {
79- status = get_values(node, obj, result);
80+ status = get_values(node, obj, json_type_string, result);
81 if (status != U1DB_OK)
82 return status;
83 }
84@@ -503,6 +504,35 @@
85 }
86
87 static int
88+op_sum(parse_tree *tree, json_object *obj, string_list *result)
89+{
90+ parse_tree *node = NULL;
91+ int status = U1DB_OK;
92+ int sum = 0;
93+ string_list *intermediate_values = NULL;
94+ string_list_item *item = NULL;
95+ char string_value[MAX_INT_STR_LEN];
96+
97+ status = init_list(&intermediate_values);
98+ if (status != U1DB_OK)
99+ goto finish;
100+ for (node = tree->first_child; node != NULL; node = node->next_sibling) {
101+ status = get_values(node, obj, json_type_int, intermediate_values);
102+ if (status != U1DB_OK)
103+ goto finish;
104+ }
105+ for (item = intermediate_values->head; item != NULL; item = item->next) {
106+ sum += atoi(item->data);
107+ }
108+ snprintf(string_value, MAX_INT_STR_LEN, "%d", sum);
109+ status = append(result, string_value);
110+finish:
111+ if (intermediate_values != NULL)
112+ destroy_list(intermediate_values);
113+ return status;
114+}
115+
116+static int
117 op_split_words(parse_tree *tree, json_object *obj, string_list *result)
118 {
119 string_list_item *item = NULL;
120@@ -514,7 +544,7 @@
121 status = init_list(&values);
122 if (status != U1DB_OK)
123 return status;
124- status = get_values(tree->first_child, obj, values);
125+ status = get_values(tree->first_child, obj, json_type_string, values);
126 if (status != U1DB_OK)
127 goto finish;
128 for (item = values->head; item != NULL; item = item->next)
129@@ -556,11 +586,6 @@
130 status = init_list(&values);
131 if (status != U1DB_OK)
132 return status;
133- status = get_values(tree->first_child, obj, values);
134- if (status != U1DB_OK)
135- goto finish;
136- //just return all the strings which have been filtered and converted from
137- //booleans by extract_field_values.
138
139 status = extract_field_values(
140 obj, tree->first_child->field_path->head, json_type_boolean, values);
141@@ -1566,7 +1591,7 @@
142 status = init_list(&values);
143 if (status != U1DB_OK)
144 goto finish;
145- status = get_values(tree, ctx->obj, values);
146+ status = get_values(tree, ctx->obj, json_type_string, values);
147 if (status != U1DB_OK)
148 goto finish;
149 for (item = values->head; item != NULL; item = item->next)
150
151=== modified file 'u1db/query_parser.py'
152--- u1db/query_parser.py 2012-07-29 17:35:21 +0000
153+++ u1db/query_parser.py 2012-07-29 17:35:21 +0000
154@@ -239,6 +239,26 @@
155 return values
156
157
158+class Sum(Transformation):
159+ """Put the sum of multiple expressions into a single index."""
160+
161+ name = "sum"
162+ # variable number of args
163+ arity = -1
164+
165+ def __init__(self, *inner):
166+ super(Sum, self).__init__(inner)
167+
168+ def get(self, raw_doc):
169+ inner_values = []
170+ for inner in self.inner:
171+ inner_values.extend(inner.get(raw_doc))
172+ return self.transform(inner_values)
173+
174+ def transform(self, values):
175+ return [sum([v for v in values if isinstance(v, int)])]
176+
177+
178 class IsNull(Transformation):
179 """Indicate whether the input is None.
180
181@@ -372,3 +392,4 @@
182 Parser.register_transormation(Bool)
183 Parser.register_transormation(IsNull)
184 Parser.register_transormation(Combine)
185+Parser.register_transormation(Sum)
186
187=== modified file 'u1db/tests/test_backends.py'
188--- u1db/tests/test_backends.py 2012-07-29 17:35:21 +0000
189+++ u1db/tests/test_backends.py 2012-07-29 17:35:21 +0000
190@@ -1660,6 +1660,21 @@
191 rows = self.db.get_from_index("index", "value2")
192 self.assertEqual([doc], rows)
193
194+ def test_get_from_index_with_sum(self):
195+ self.db.create_index("index", "number(sum(foo, bar), 5)")
196+ content = '{"foo": 12, "bar": [23, 7]}'
197+ doc = self.db.create_doc_from_json(content)
198+ content = '{"foo": 12, "bar": [23, 7]}'
199+ rows = self.db.get_from_index("index", "00042")
200+ self.assertEqual([doc], rows)
201+
202+ def test_get_from_index_with_sum_ignores_non_numeric(self):
203+ self.db.create_index("index", "number(sum(foo, bar), 5)")
204+ content = '{"foo": "12", "bar": [23, 7]}'
205+ doc = self.db.create_doc_from_json(content)
206+ rows = self.db.get_from_index("index", "00030")
207+ self.assertEqual([doc], rows)
208+
209 def test_get_complex_combine(self):
210 self.db.create_index(
211 "index", "combine(number(foo, 5), lower(bar), split_words(baz))")
212
213=== modified file 'u1db/tests/test_query_parser.py'
214--- u1db/tests/test_query_parser.py 2012-07-29 17:35:21 +0000
215+++ u1db/tests/test_query_parser.py 2012-07-29 17:35:21 +0000
216@@ -65,6 +65,12 @@
217 'combine(lower(field1), split_words(field2), '
218 'number(field3, 5))'), query_parser.Combine)
219
220+ def test_nested_branching_mapping2(self):
221+ self.assertIsInstance(
222+ self.parser.parse(
223+ 'number(sum(field1, sub.field2, field3), 6)'),
224+ query_parser.Number)
225+
226 def test_single_mapping_multiple_fields(self):
227 self.assertIsInstance(
228 self.parser.parse('number(field1, 5)'), query_parser.Number)

Subscribers

People subscribed via source and target branches