-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfeed.xml
435 lines (378 loc) · 23.1 KB
/
feed.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Blog Name</title>
<subtitle>Blog subtitle</subtitle>
<id>http://saharaying.github.com/blog</id>
<link href="http://blog.url.com/"/>
<link href="http://blog.url.com/feed.xml" rel="self"/>
<updated>2014-03-15T06:51:00Z</updated>
<author>
<name>Fu Ying</name>
</author>
<entry>
<title>MH370</title>
<link rel="alternate" href="/2014/03/15/mh370.html"/>
<id>/2014/03/15/mh370.html</id>
<published>2014-03-15T06:51:00Z</published>
<updated>2014-03-15T06:51:00Z</updated>
<author>
<name>Fu Ying</name>
</author>
<summary type="html"><p>马航MH370失联,8天了,不知道多少反反复复,坠海 -&gt; 空中解体 -&gt; 劫机,越南发消息 -&gt; 中方转发 -&gt; 马方否认,真相到底是什么?现在看来,
劫机的可能性最大,而且很有可能是飞行员啊,但又让人不解的是,劫机的话,却没有人跳出来说要跟政府谈判,想要什么你说呀,你不说我们怎么知道呀。</p>
<p>还是等等继续看马航新闻发布会直播吧</p>
</summary>
<content type="html"><p>马航MH370失联,8天了,不知道多少反反复复,坠海 -&gt; 空中解体 -&gt; 劫机,越南发消息 -&gt; 中方转发 -&gt; 马方否认,真相到底是什么?现在看来,
劫机的可能性最大,而且很有可能是飞行员啊,但又让人不解的是,劫机的话,却没有人跳出来说要跟政府谈判,想要什么你说呀,你不说我们怎么知道呀。</p>
<p>还是等等继续看马航新闻发布会直播吧</p>
</content>
</entry>
<entry>
<title>Cross Domain iFrame Auto Height</title>
<link rel="alternate" href="/2013/04/30/cross-domain-iframe-auto-height.html"/>
<id>/2013/04/30/cross-domain-iframe-auto-height.html</id>
<published>2013-04-30T08:00:00Z</published>
<updated>2013-04-30T08:00:00Z</updated>
<author>
<name>Fu Ying</name>
</author>
<summary type="html"><p>We have a feature, that is providing a js script which generates an iframe containing a form, so that other people can embed this form on their website by just including this js.
What we did before, is calculating the form height, and writing the height...</p></summary>
<content type="html"><p>We have a feature, that is providing a js script which generates an iframe containing a form, so that other people can embed this form on their website by just including this js.
What we did before, is calculating the form height, and writing the height into the script, then the host website could know how much the iframe's height should be.
However sometimes people will change the form, so the height will be changed! Then they need to update the script on their website, otherwise there will be an ugly scroll bar when the height increases.
This is really rebarbative 'cause we can't force our user to update the script every time they change the form.</p>
<p>A few days ago, we assigned this issue with a high priority, which was making the iframe set it's height automatically by it's own content height.</p>
<p>So the hardest problem is how can we deal with the cross domain security issue, since we can't get the iframe content height from the parent page if they are in different domain.
Well luckily, after a few digging, we came up with the <strong>postMessage</strong> way! Can't visit the content window of a different domain? fine, then we can send a message from the iframe!</p>
<p>Here is the code:</p>
<p>in the iframe, add the following script(in coffee)</p>
<pre>
<code>:::javascript
$ -&gt;
postSize = -&gt;
target = if parent.postMessage
parent
else
if parent.document.postMessage then parent.document else undefined
target.postMessage $(document).height(), "*" if target
if window.addEventListener
window.addEventListener "load", postSize, false
else
window.attachEvent "onload", postSize, false if window.attachEvent
</code>
</pre>
<p>Let me explain the above code. There is a <em>postSize</em> function, which finds the parent, then posts the height as a message to the parent.
This postSize function will be bound as soon as the window loaded. <em>window.attachEvent</em> is used to deal with the compatibility of IE.</p>
<p>Then in the parent site, add the following script before the iframe</p>
<pre>
<code>:::javascript
receiveSize = function (e) {
if ('&lt; your iframe domain &gt;'.indexOf(e.origin) === 0) {
// to make sure it's the message posted from the right iframe
document.getElementById(iframeId).style.height = e.data + 'px';
}
};
if (window.addEventListener) {
window.addEventListener('message', receiveSize, false);
} else if (window.attachEvent) {
window.attachEvent('onmessage', receiveSize, false);
}
</code>
</pre>
<p>Here we use the pure javascript, similar, the parent site listens to the message event, then sets the iframe height.</p>
<p>Voila, we achieved it ~ Then we no longer suffered the pain of iframe height ~</p>
</content>
</entry>
<entry>
<title>MongoMapper迁移到Mongoid</title>
<link rel="alternate" href="/2013/04/30/migrate-from-mongomapper-to-mongoid.html"/>
<id>/2013/04/30/migrate-from-mongomapper-to-mongoid.html</id>
<published>2013-04-30T07:47:00Z</published>
<updated>2013-04-30T07:47:00Z</updated>
<author>
<name>Fu Ying</name>
</author>
<summary type="html"><p>这篇博客断断续续写了将近一个月,终于算是总结的差不多了,将就着看吧。</p>
<hr><p>原本我们使用了MongoMapper作为MongoDB的ORM,但是随着业务逻辑越来越复杂,它已经不能满足我们的需求了:</p>
<ol>
<li>数据量越来越大,数据库查询性能问题越来越显著,据说Mongoid的性能可以提高4倍</li>
<li>MongoMapper的gem最后一次更新是在2012年9月,维护的频率不如Mongoid(最后更新2013年4月17日),并且对动态域的存取操作支持也不好</li>
<li>围绕Mongoid的周边gem更加丰富</li>
</ol><p>于是我花...</p></summary>
<content type="html"><p>这篇博客断断续续写了将近一个月,终于算是总结的差不多了,将就着看吧。</p>
<hr />
<p>原本我们使用了MongoMapper作为MongoDB的ORM,但是随着业务逻辑越来越复杂,它已经不能满足我们的需求了:</p>
<ol>
<li>数据量越来越大,数据库查询性能问题越来越显著,据说Mongoid的性能可以提高4倍</li>
<li>MongoMapper的gem最后一次更新是在2012年9月,维护的频率不如Mongoid(最后更新2013年4月17日),并且对动态域的存取操作支持也不好</li>
<li>围绕Mongoid的周边gem更加丰富</li>
</ol>
<p>于是我花了大概4天的时间将项目从MongoMapper迁移到了Mongoid。能看得到的明显的变化有:</p>
<ul>
<li>数据库查询确实快了很多,包括聚合查询的速度也有很大提升。</li>
<li>调用<em>update_attributes</em>方法,不再是使用MongoDB的<em>$update</em>更新所有域,而是使用<em>$set</em>仅更新有变化的域。</li>
</ul>
<p>在迁移过程中,需要注意以下几个方面:</p>
<h4 id="section">1. 数据库连接配置</h4>
<p>在mongid.yml中,我们配置了两个option: <em>include_type_for_serialization: true</em> 和<em>raise_not_found_error: false</em>。</p>
<p>第一个是指在对model进行序列化时(比如to_json),默认包含Single Collection Inheritance的_type字段。</p>
<p>第二个顾名思义,当使用find方法查询model时,如果找不到,返回nil,而不是抛出异常。</p>
<pre>
<code>:::yaml
development:
sessions:
default:
hosts:
- 127.0.0.1:27017
database: goldendata_development
username: root
password:
options:
include_type_for_serialization: true
raise_not_found_error: false
</code>
</pre>
<h4 id="modelrelation">2. Model中的字段和relation</h4>
<ul>
<li>field和fields是关键字,不能作为属性或relation的名字,如果数据库里对应的key为<em>fields</em>,那么你需要<strong>store_as</strong>这个option。譬如
<em>embeds_many :f_fields, store_as: 'fields', class_name: 'Field'</em></li>
<li>如果要给model加上时间戳,只需要<strong>include Mongoid::Timestamps</strong>这个module就可以了</li>
<li>没有userstamps的module,如果真的需要,只好自己实现</li>
<li>Mongoid<strong>支持动态域的存取</strong>,也就是说,不用<em>field</em>关键字声明的属性也可以被访问和保存,只需要调用<em>read_attribute</em>或<em>write_attribute</em>方法即可,譬如 <em>model.read_attribute(:dynamic_field)</em> 或者 <em>model[:dynamic_field]</em>,如果动态域不存在,则会返回nil。</li>
</ul>
<h4 id="section-1">3. 查询和聚合</h4>
<ul>
<li>复杂的查询由<strong><a href="http://mongoid.org/en/origin/docs/selection.html">origin</a></strong>提供,包括and/or/elem_match/gt等一系列方法。譬如
<em>Entry.or({field1: 'a'}, {field2: 'b'})</em></li>
<li>sort需要替换成对应的<strong>asc</strong>/<strong>desc</strong>或<strong>order_by</strong>,比如<em>desc(:created_at)</em> 或者 <em>order_by(:created_at.desc)</em></li>
<li>另外,Mongoid不再支持group方法,应该改用aggregate或者map_reduce实现</li>
</ul>
<h4 id="model">4. 其他model相关的变更</h4>
<ul>
<li>异常。<em>MongoMapper::DocumentNotFound</em> 异常要换成 <em>Mongoid::Errors::DocumentNotFound</em></li>
<li><em>to_mongo</em>和<em>from_mongo</em>方法要分别换成<strong>mongoize</strong>和<strong>demongoize</strong></li>
<li>对model进行序列化时,默认的与id对于的key不再是<em>id</em>,而是<em>_id</em></li>
<li>对于callback,譬如after_create,如果这个model上含有has_many的relation,并且该relation设置为autosave: true,那么这个after_create的callback将会发生在该model创建以后,但是在这个has_many的关联保存之前。参见:
&gt; Note that to be efficient, Mongoid only fires the callback of the document that the persistence action was executed on. This is that Mongoid aims to support large hierarchies and to handle optimized atomic updates callbacks can't be firing all over the document hierarchy.</li>
<li>Mongoid对model有着更严格的检查,比如我们不能直接创建一个embedded document对象</li>
<li>创建<strong>索引</strong>的方式改变。在Mongid中,调用<em>Form.index(creator_id: 1, created_at: -1)</em>不会真正创建索引,必须运行<em>rake db:mongoid:create_indexes</em>,而要移除索引,运行<em>rake db:mongoid:remove_indexes</em>。</li>
</ul>
<h4 id="ormgem">5. ORM周边的gem替换</h4>
<p>如果使用了一些需要依赖MongoDB的gem,比如omniauth-identity, will_paginate, mm-carrierwave, sidekiq等,
- <em>include OmniAuth::Identity::Models::MongoMapper</em> 变为 <em>include OmniAuth::Identity::Models::Mongoid</em>
- will_paginate gem换成will_paginate_mongoid
- mm-carrierwave gem换成carrierwave-mongoid,但是如果只是声明成<em>mount_uploader &lt;mounted_as&gt;, Uploader</em>,文件名将会存储到&lt;mounted_as&gt;字段,而不是像mm-carrierwave那样存储到&lt;mounted_as&gt;_filename。为了不进行数据迁移,我们可以加上<strong>mount_on</strong>的声明:
<em>mount_uploader &lt;mounted_as&gt;, Uploader, mount_on: &lt;mounted_as&gt;_filename</em>。
- 如果使用了sidekiq,必须确保每个sidekiq的worker运行完后释放数据库的连接,那么可能需要使用<strong><a href="http://mongoid.org/en/mongoid/docs/tips.html#sidekiq">kiqstand</a></strong>。</p>
</content>
</entry>
<entry>
<title>MongoDB中计算聚合值的三种方法(三)</title>
<link rel="alternate" href="/2013/02/09/3-ways-to-calculate-aggregation-in-mongodb-3.html"/>
<id>/2013/02/09/3-ways-to-calculate-aggregation-in-mongodb-3.html</id>
<published>2013-02-09T12:35:00Z</published>
<updated>2013-02-09T12:35:00Z</updated>
<author>
<name>Fu Ying</name>
</author>
<summary type="html"><p>其实,MongoDB已经提供了求最大最小平均值和和值的方法,所以连reduce都可以免了,我们可以直接使用aggregation框架,这就是第三种方法。</p>
<h4>3. 使用aggregation框架</h4>
<p>MongoDB文档中<a href="http://docs.mongodb.org/manual/reference/aggregation/">aggregation这一节</a>对这个框架的使用有详细的说明。结合我们的例子,可以这样使用:</p>
<pre>
<code>:::ruby
def number_statistics field, condition
self.collection.aggregate([
{:$match =&gt;...</code></pre></summary>
<content type="html"><p>其实,MongoDB已经提供了求最大最小平均值和和值的方法,所以连reduce都可以免了,我们可以直接使用aggregation框架,这就是第三种方法。</p>
<h4>3. 使用aggregation框架</h4>
<p>MongoDB文档中<a href="http://docs.mongodb.org/manual/reference/aggregation/">aggregation这一节</a>对这个框架的使用有详细的说明。结合我们的例子,可以这样使用:</p>
<pre>
<code>:::ruby
def number_statistics field, condition
self.collection.aggregate([
{:$match =&gt; condition.merge(field =&gt; {:$ne =&gt; nil})},
{:$group =&gt; {
:_id =&gt; "$form_id",
:min =&gt; {:$min =&gt; "$#{field}"},
:max =&gt; {:$max =&gt; "$#{field}"},
:sum =&gt; {:$sum =&gt; "$#{field}"},
:avg =&gt; {:$avg =&gt; "$#{field}"}
}},
{:$project =&gt; {:_id =&gt; 0, :min =&gt; 1, :max =&gt; 1, :sum =&gt; 1, :avg =&gt; 1}}
]).first
end
</code>
</pre>
<p>下面对这段代码做以说明,用到了3个操作符:$match,$group和$project。</p>
<ol>
<li>
<h5>$match</h5>
对需要进行聚合运算的文档进行过滤,MongoDB中接受一个javascript object作为值,如果使用MongoMapper,那么可以是一个hash。
我们这里需要过滤掉空值,也就是说不希望空值参与聚合计算,所以有 <code>field =&gt; {:$ne =&gt; nil}</code>。
</li>
<li>
<h5>$group</h5>
我们需要的运算都在这里了,_id是必须的,表明要根据什么进行group,接下来就是要计算并返回的min、max、sum和avg。
</li>
<li>
<h5>$project</h5>
这里对返回结果进行了打磨,<code>:_id =&gt; 0</code> 表明我们在最终结果里去掉了_id,而其他计算值则会返回。
</li>
</ol>
<p>需要注意一点的是,$match是pipeline中的第一个操作,这样就可以在做聚合之前先过滤掉我们不需要的文档,提高处理的速度。</p>
<p>Voilà, 就到这里吧,大过年的,休息,休息,休息一下~</p>
</content>
</entry>
<entry>
<title>MongoDB中计算聚合值的三种方法(二)</title>
<link rel="alternate" href="/2013/01/03/3-ways-to-calculate-aggregation-in-mongodb-2.html"/>
<id>/2013/01/03/3-ways-to-calculate-aggregation-in-mongodb-2.html</id>
<published>2013-01-03T05:36:00Z</published>
<updated>2013-01-03T05:36:00Z</updated>
<author>
<name>Fu Ying</name>
</author>
<summary type="html"><p>在<a href="/2012/12/31/3-ways-to-calculate-aggregation-in-mongodb-1.html">上一篇博文</a>中,介绍了如何使用Map-Reduce计算聚合值。
但我们的需求并不需要进行任何的映射,我们只要对过滤出来的结果进行计算就可以了。这样,MongoDB中的group方法就可以直接为我所用。</p>
<h4>2. 使用group</h4>
<p>逻辑上跟使用Map-Reduce差不多,不过reduce和finalize的参数有一些变化。</p>
<pre>
<code>:::ruby
def number_statistics field, condition
self.collection.group(
:cond =...</code></pre></summary>
<content type="html"><p>在<a href="/2012/12/31/3-ways-to-calculate-aggregation-in-mongodb-1.html">上一篇博文</a>中,介绍了如何使用Map-Reduce计算聚合值。
但我们的需求并不需要进行任何的映射,我们只要对过滤出来的结果进行计算就可以了。这样,MongoDB中的group方法就可以直接为我所用。</p>
<h4>2. 使用group</h4>
<p>逻辑上跟使用Map-Reduce差不多,不过reduce和finalize的参数有一些变化。</p>
<pre>
<code>:::ruby
def number_statistics field, condition
self.collection.group(
:cond =&gt; condition,
:reduce =&gt; number_reduce(field),
:initial =&gt; {sum: 0, count: 0},
:finalize =&gt; finalize
).find.first
end
</code>
</pre>
<p>上面是group方法的参数结构。cond与Map-Reduce中的query参数一样,是查询条件;增加了initial,其值为结果初始对象。我们这里将和值和计数置为0。</p>
<p>reduce方法的参数则为当前文档(current)和结果对象(result);而在Map-Reduce中,第一个参数为key,第二个参数为map方法中映射到该key的所有对象。</p>
<pre>
<code>:::ruby
def number_reduce field
&lt;&lt;-REDUCE
function(current, result) {
var value = current.#{field};
if (result.min == null || result.min &gt; value) result.min = value;
if (result.max == null || result.max &lt; value) result.max = value;
result.sum += value;
result.count++;
}
REDUCE
end
</code>
</pre>
<p>finalize方法也有所不同,不需要返回值,传递的参数即为结果对象,我们只需要对其进行修改就可以了:</p>
<pre>
<code>:::ruby
def finalize
&lt;&lt;-FINALIZE
function(result) {
result.avg = result.sum / result.count;
delete result.count;
}
FINALIZE
end
</code>
</pre>
<p>相比较上一种方法,此方法已简单许多,但是对于复杂的聚合逻辑,它也许就不如Map-Reduce能驾驭了。</p>
</content>
</entry>
<entry>
<title>MongoDB中计算聚合值的三种方法(一)</title>
<link rel="alternate" href="/2012/12/31/3-ways-to-calculate-aggregation-in-mongodb-1.html"/>
<id>/2012/12/31/3-ways-to-calculate-aggregation-in-mongodb-1.html</id>
<published>2012-12-31T14:25:00Z</published>
<updated>2012-12-31T14:25:00Z</updated>
<author>
<name>Fu Ying</name>
</author>
<summary type="html"><p>说到报表,不可避免的会涉及到计算数字域的总计、平均值、最大值和最小值。前一阵在做<a href="http://weibo.com/u/3166713675" target="_blank">@金数据</a>的过程中,对使用MongoMapper做聚合做了一点点研究。总的来说,应该是有三种方法:</p>
<h4>1. 使用Map-Reduce</h4>
<p>MongoDB的Map-Reduce可谓是功能强大,可以处理复杂的聚合逻辑。关键是map方法和reduce方法的设计。</p>
<pre>
<code>:::ruby
def map field
&lt;&lt;-MAP
function() {
var number = this.#{field...</code></pre></summary>
<content type="html"><p>说到报表,不可避免的会涉及到计算数字域的总计、平均值、最大值和最小值。前一阵在做<a href="http://weibo.com/u/3166713675" target="_blank">@金数据</a>的过程中,对使用MongoMapper做聚合做了一点点研究。总的来说,应该是有三种方法:</p>
<h4>1. 使用Map-Reduce</h4>
<p>MongoDB的Map-Reduce可谓是功能强大,可以处理复杂的聚合逻辑。关键是map方法和reduce方法的设计。</p>
<pre>
<code>:::ruby
def map field
&lt;&lt;-MAP
function() {
var number = this.#{field};
emit(1, {min: number, max: number, sum: number, count: 1});
}
MAP
end
</code>
</pre>
<p>这里,我们不需要对任何域做group,所以emit的第一个参数就给了1,而第二个参数则构造了一个包含最大值、最小值、和值和计数的对象,这个对象的结构必须跟下面的reduce方法返回的对象结构一致:</p>
<pre>
<code>:::ruby
def reduce
&lt;&lt;-REDUCE
function(key, values) {
var firstValue = values[0];
var result = {min: firstValue.min, max: firstValue.max, sum: 0, count: 0};
values.forEach( function (value) {
if (value.min &lt; result.min) result.min = value.min;
if (value.max &gt; result.max) result.max = value.max;
result.count += value.count;
result.sum += value.sum;
});
return result;
}
REDUCE
end
</code>
</pre>
<p>最后,我们就可以利用finalize方法根据和值和计数求得平均值:</p>
<pre>
<code>:::ruby
def finalize
&lt;&lt;-FINALIZE
function(key, result) {
result.avg = result.sum / result.count;
delete result.count;
return result;
}
FINALIZE
end
</code>
</pre>
<p>上面不论是reduce方法还是finalize方法,定义的js function中的第一个参数key,都是与map方法中emit的第一个参数对应的,也就是我们需要按其进行分组的域。
例如我们要对数据的日期进行分组,那么在map方法中,emit的第一个参数就应该为this.date_field。</p>
<p>如此,就可以调用MongoMapper的API了:</p>
<pre>
<code>:::ruby
def number_statistics field, condition
self.collection.map_reduce(
map(field), reduce,
:out =&gt; "number_statistics_result",
:query =&gt; condition,
:finalize =&gt; finalize
).find.first
end
</code>
</pre>
</content>
</entry>
</feed>