我有一个API,我从中提取数据,我想收集此API中的所有标记...但我不知道预先标记的数量,并且API通过最大数量限制访问在任何1次调用中返回的结果(100)。它有无限数量的页面。
所以调用可能如下所示:Tag.update_tags(100, 5)
其中100
是1次调用中返回的最大对象数,5
是要开始的页面(即如果您认为标签按顺序存储,这就是返回ID为401 - 500
范围的标签记录。
问题是,我不想手动输入5
(即我不知道上限是多少)。我没办法ping标签的总数(如果有的话,我会简单地将它分开并将这个调用放在一个循环中直到那个数字)。
我所知道的是,一旦它到达没有任何结果的页面,它将返回一个空数组[]
。
那么,如何循环遍历所有标记并在返回的结果为空数组时停止(这将返回最终结果,因此不进行评估)?
那个循环是什么样的?
答案 0 :(得分:1)
当结果返回空数组时,使用带有break语句的无条件循环。
i = 1
loop do
result = call_to_api(i)
do_something_with(result)
i += 1
break if result.empty?
end
当然,在生产场景中,您需要更强大的功能,包括异常处理程序,一些进度日志报告以及某种具体的迭代限制,以确保循环不会变为无限。
<强>更新强>
这是一个使用类来包装逻辑的例子。
class Api
DEFAULT_OPTIONS = {:start_position => 1, :max_iterations => 1000}
def initialize(base_uri, config)
@config = DEFAULT_OPTIONS.merge(config)
@position = config[:start_position]
@results_count = 0
end
def each(&block)
advance(&block) while can_advance?
log("Processed #{@results_count} results")
end
def advance(&block)
yield result
@results_count += result.count
@position += 1
@current_result = nil
end
def result
@current_result ||= begin
response = Net::HTTP.get_response(current_uri)
JSON.decode(response.body)
rescue
# provide some exception handling/logging
end
end
def can_advance?
@position < (@config[:start_position] + @config[:max_iterations]) && result.any?
end
def current_uri
Uri.parse("#{@base_uri}?page=#{@position}")
end
end
api = Api.new('http://somesite.com/api/v1/resource')
api.each do |result|
do_something_with(result)
end
通过设置每个线程的开始和迭代计数,还可以通过设置每个线程的开始和迭代计数来实现并发,这可以通过并发的http请求来加快速度。
答案 1 :(得分:0)
嗯。您可以一次获得100个项目,并从特定页面开始。如何实现迭代取决于您想要做什么。我们假设您要收集所有唯一标记。建立一个映射(例如,HashMap),然后一次检索一个页面并对其进行处理。当你点击一个空的页面时,你已经完成了。
// Implements a map and methods to update it
MyHashMap uniqueTags;
// Stores a page of tags
Page page;
Do
// get a page of tags
page = readTags();
if (page != null) {
uniqueTags.getUniqueTags(page);
} else {
break;
}
until (page == null);