fluent-plugin-droonga 0.9.0 → 0.9.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +3 -0
- data/Gemfile +8 -1
- data/fluent-plugin-droonga.gemspec +2 -2
- data/lib/droonga/adapter.rb +39 -0
- data/lib/droonga/adapter_runner.rb +99 -0
- data/lib/droonga/catalog/base.rb +11 -11
- data/lib/droonga/catalog/dataset.rb +54 -0
- data/lib/droonga/catalog/version1.rb +1 -1
- data/lib/droonga/collector.rb +5 -7
- data/lib/droonga/collector_plugin.rb +7 -7
- data/lib/droonga/command.rb +36 -0
- data/lib/droonga/{plugin/input_adapter/crud.rb → command_repository.rb} +14 -8
- data/lib/droonga/dispatcher.rb +86 -54
- data/lib/droonga/distributed_command_planner.rb +183 -0
- data/lib/droonga/distributor.rb +43 -17
- data/lib/droonga/handler.rb +13 -72
- data/lib/droonga/handler_message.rb +5 -5
- data/lib/droonga/handler_messenger.rb +4 -1
- data/lib/droonga/handler_plugin.rb +2 -2
- data/lib/droonga/handler_runner.rb +104 -0
- data/lib/droonga/input_message.rb +4 -4
- data/lib/droonga/legacy_pluggable.rb +66 -0
- data/lib/droonga/{input_adapter.rb → legacy_plugin.rb} +27 -22
- data/lib/droonga/{plugin_repository.rb → legacy_plugin_repository.rb} +2 -4
- data/lib/droonga/message_matcher.rb +101 -0
- data/lib/droonga/{input_adapter_plugin.rb → planner.rb} +14 -10
- data/lib/droonga/planner_plugin.rb +54 -0
- data/lib/droonga/pluggable.rb +9 -45
- data/lib/droonga/plugin.rb +9 -33
- data/lib/droonga/plugin/collector/basic.rb +2 -0
- data/lib/droonga/plugin/collector/search.rb +31 -37
- data/lib/droonga/plugin/{handler/groonga/table_remove.rb → metadata/adapter_message.rb} +23 -18
- data/lib/droonga/plugin/{handler/search.rb → metadata/handler_action.rb} +19 -15
- data/lib/droonga/plugin/metadata/input_message.rb +39 -0
- data/lib/droonga/plugin/planner/crud.rb +49 -0
- data/lib/droonga/plugin/{distributor → planner}/distributed_search_planner.rb +62 -70
- data/lib/droonga/plugin/{distributor → planner}/groonga.rb +11 -32
- data/lib/droonga/plugin/{distributor → planner}/search.rb +5 -5
- data/lib/droonga/plugin/{distributor → planner}/watch.rb +15 -6
- data/lib/droonga/plugin_loader.rb +10 -0
- data/lib/droonga/plugin_registerable.rb +34 -10
- data/lib/droonga/plugin_registry.rb +58 -0
- data/lib/droonga/plugins/crud.rb +124 -0
- data/lib/droonga/plugins/error.rb +50 -0
- data/lib/droonga/{output_adapter_plugin.rb → plugins/groonga.rb} +9 -13
- data/lib/droonga/plugins/groonga/column_create.rb +123 -0
- data/lib/droonga/plugins/groonga/generic_command.rb +65 -0
- data/lib/droonga/{plugin/output_adapter/groonga.rb → plugins/groonga/generic_response.rb} +16 -15
- data/lib/droonga/plugins/groonga/select.rb +124 -0
- data/lib/droonga/plugins/groonga/table_create.rb +106 -0
- data/lib/droonga/plugins/groonga/table_remove.rb +57 -0
- data/lib/droonga/plugins/search.rb +40 -0
- data/lib/droonga/plugins/watch.rb +156 -0
- data/lib/droonga/processor.rb +8 -10
- data/lib/droonga/searcher.rb +14 -4
- data/lib/droonga/searcher/mecab_filter.rb +67 -0
- data/lib/droonga/session.rb +5 -5
- data/lib/droonga/test.rb +1 -1
- data/lib/droonga/test/stub_handler_message.rb +1 -1
- data/lib/droonga/test/{stub_distributor.rb → stub_planner.rb} +1 -1
- data/lib/droonga/worker.rb +7 -8
- data/lib/fluent/plugin/out_droonga.rb +0 -1
- data/sample/cluster/catalog.json +2 -4
- data/sample/mecab_filter/data.grn +7 -0
- data/sample/mecab_filter/ddl.grn +7 -0
- data/sample/mecab_filter/search_with_mecab_filter.json +21 -0
- data/sample/mecab_filter/search_without_mecab_filter.json +21 -0
- data/test/command/config/default/catalog.json +2 -5
- data/test/command/suite/search/error/no-query.expected +13 -0
- data/test/command/suite/search/error/no-query.test +7 -0
- data/test/command/suite/search/error/unknown-source.expected +26 -0
- data/test/command/suite/watch/subscribe.expected +3 -3
- data/test/command/suite/watch/unsubscribe.expected +3 -3
- data/test/unit/catalog/test_dataset.rb +385 -0
- data/test/unit/catalog/test_version1.rb +111 -45
- data/test/unit/fixtures/catalog/version1.json +0 -3
- data/test/unit/helper.rb +2 -1
- data/test/unit/helper/distributed_search_planner_helper.rb +83 -0
- data/test/unit/plugin/collector/test_basic.rb +233 -376
- data/test/unit/plugin/collector/test_search.rb +8 -17
- data/test/unit/plugin/planner/search_planner/test_basic.rb +120 -0
- data/test/unit/plugin/planner/search_planner/test_group_by.rb +573 -0
- data/test/unit/plugin/planner/search_planner/test_output.rb +388 -0
- data/test/unit/plugin/planner/search_planner/test_sort_by.rb +938 -0
- data/test/unit/plugin/{distributor → planner}/test_search.rb +20 -75
- data/test/unit/{plugin/handler → plugins/crud}/test_add.rb +11 -11
- data/test/unit/plugins/groonga/select/test_adapter_input.rb +213 -0
- data/test/unit/{plugin/output_adapter/groonga/test_select.rb → plugins/groonga/select/test_adapter_output.rb} +12 -13
- data/test/unit/{plugin/handler → plugins}/groonga/test_column_create.rb +20 -5
- data/test/unit/{plugin/handler → plugins}/groonga/test_table_create.rb +5 -0
- data/test/unit/{plugin/handler → plugins}/groonga/test_table_remove.rb +8 -1
- data/test/unit/{plugin/handler → plugins}/test_groonga.rb +5 -5
- data/test/unit/{plugin/handler → plugins}/test_search.rb +21 -5
- data/test/unit/{plugin/handler → plugins}/test_watch.rb +29 -10
- data/{lib/droonga/command_mapper.rb → test/unit/test_command_repository.rb} +16 -22
- data/test/unit/{test_plugin.rb → test_legacy_plugin.rb} +3 -3
- data/test/unit/{test_plugin_repository.rb → test_legacy_plugin_repository.rb} +3 -3
- data/test/unit/test_message_matcher.rb +137 -0
- metadata +86 -66
- data/bin/grn2jsons +0 -82
- data/lib/droonga/distribution_planner.rb +0 -76
- data/lib/droonga/distributor_plugin.rb +0 -95
- data/lib/droonga/output_adapter.rb +0 -53
- data/lib/droonga/plugin/collector/groonga.rb +0 -83
- data/lib/droonga/plugin/distributor/crud.rb +0 -84
- data/lib/droonga/plugin/handler/add.rb +0 -109
- data/lib/droonga/plugin/handler/forward.rb +0 -75
- data/lib/droonga/plugin/handler/groonga.rb +0 -99
- data/lib/droonga/plugin/handler/groonga/column_create.rb +0 -106
- data/lib/droonga/plugin/handler/groonga/table_create.rb +0 -91
- data/lib/droonga/plugin/handler/watch.rb +0 -108
- data/lib/droonga/plugin/input_adapter/groonga.rb +0 -49
- data/lib/droonga/plugin/input_adapter/groonga/select.rb +0 -63
- data/lib/droonga/plugin/output_adapter/crud.rb +0 -51
- data/lib/droonga/plugin/output_adapter/groonga/select.rb +0 -54
- data/lib/groonga_command_converter.rb +0 -143
- data/sample/fluentd.conf +0 -8
- data/test/unit/plugin/distributor/test_search_planner.rb +0 -1102
- data/test/unit/plugin/input_adapter/groonga/test_select.rb +0 -248
- data/test/unit/test_command_mapper.rb +0 -44
- data/test/unit/test_groonga_command_converter.rb +0 -242
data/lib/droonga/processor.rb
CHANGED
@@ -1,6 +1,4 @@
|
|
1
|
-
#
|
2
|
-
#
|
3
|
-
# Copyright (C) 2013 Droonga Project
|
1
|
+
# Copyright (C) 2013-2014 Droonga Project
|
4
2
|
#
|
5
3
|
# This library is free software; you can redistribute it and/or
|
6
4
|
# modify it under the terms of the GNU Lesser General Public
|
@@ -15,7 +13,7 @@
|
|
15
13
|
# License along with this library; if not, write to the Free Software
|
16
14
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
17
15
|
|
18
|
-
require "droonga/
|
16
|
+
require "droonga/handler_runner"
|
19
17
|
|
20
18
|
module Droonga
|
21
19
|
class Processor
|
@@ -27,24 +25,24 @@ module Droonga
|
|
27
25
|
end
|
28
26
|
|
29
27
|
def start
|
30
|
-
@
|
31
|
-
@
|
28
|
+
@handler_runner = HandlerRunner.new(@loop, @options)
|
29
|
+
@handler_runner.start
|
32
30
|
end
|
33
31
|
|
34
32
|
def shutdown
|
35
33
|
$log.trace("#{log_tag}: shutdown: start")
|
36
|
-
@
|
34
|
+
@handler_runner.shutdown
|
37
35
|
$log.trace("#{log_tag}: shutdown: done")
|
38
36
|
end
|
39
37
|
|
40
38
|
def process(message)
|
41
39
|
$log.trace("#{log_tag}: process: start")
|
42
40
|
command = message["type"]
|
43
|
-
if @
|
41
|
+
if @handler_runner.processable?(command)
|
44
42
|
$log.trace("#{log_tag}: process: handlable: #{command}")
|
45
|
-
synchronous = @
|
43
|
+
synchronous = @handler_runner.prefer_synchronous?(command)
|
46
44
|
if @n_workers.zero? or synchronous
|
47
|
-
@
|
45
|
+
@handler_runner.process(message)
|
48
46
|
else
|
49
47
|
@message_pusher.push(message)
|
50
48
|
end
|
data/lib/droonga/searcher.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
#
|
3
|
-
# Copyright (C) 2013 Droonga Project
|
3
|
+
# Copyright (C) 2013-2014 Droonga Project
|
4
4
|
#
|
5
5
|
# This library is free software; you can redistribute it and/or
|
6
6
|
# modify it under the terms of the GNU Lesser General Public
|
@@ -20,9 +20,16 @@ require "tsort"
|
|
20
20
|
require "groonga"
|
21
21
|
|
22
22
|
require "droonga/time_formatter"
|
23
|
+
require "droonga/message_processing_error"
|
23
24
|
|
24
25
|
module Droonga
|
25
26
|
class Searcher
|
27
|
+
class NoQuery < BadRequest
|
28
|
+
def initialize
|
29
|
+
super("You must specify one or more query.")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
26
33
|
class MissingSourceParameter < BadRequest
|
27
34
|
def initialize(query, queries)
|
28
35
|
super("The query #{query.inspect} has no source. " +
|
@@ -63,9 +70,8 @@ module Droonga
|
|
63
70
|
private
|
64
71
|
def process_queries(queries)
|
65
72
|
$log.trace("#{log_tag}: process_queries: start")
|
66
|
-
|
67
|
-
|
68
|
-
return {}
|
73
|
+
if queries.nil? || queries.empty?
|
74
|
+
raise NoQuery.new
|
69
75
|
end
|
70
76
|
$log.trace("#{log_tag}: process_queries: sort: start")
|
71
77
|
sorted_queries = QuerySorter.sort(queries)
|
@@ -640,3 +646,7 @@ module Droonga
|
|
640
646
|
end
|
641
647
|
end
|
642
648
|
end
|
649
|
+
|
650
|
+
if ENV["DROONGA_ENABLE_SEARCH_MECAB_FILTER"] == "yes"
|
651
|
+
require "droonga/searcher/mecab_filter"
|
652
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# Copyright (C) 2014 Droonga Project
|
4
|
+
#
|
5
|
+
# This library is free software; you can redistribute it and/or
|
6
|
+
# modify it under the terms of the GNU Lesser General Public
|
7
|
+
# License version 2.1 as published by the Free Software Foundation.
|
8
|
+
#
|
9
|
+
# This library is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
12
|
+
# Lesser General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU Lesser General Public
|
15
|
+
# License along with this library; if not, write to the Free Software
|
16
|
+
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
17
|
+
|
18
|
+
require "MeCab"
|
19
|
+
|
20
|
+
module Droonga
|
21
|
+
class Searcher
|
22
|
+
class QuerySearcher
|
23
|
+
class MeCabTokenizer
|
24
|
+
def initialize
|
25
|
+
@mecab = MeCab::Tagger.new("-Owakati")
|
26
|
+
end
|
27
|
+
|
28
|
+
def tokenize(text)
|
29
|
+
tokens = @mecab.parse(text).force_encoding("utf-8").split(/\s+/)
|
30
|
+
tokens.reject do |token|
|
31
|
+
token.empty?
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def apply_mecab_filter(condition)
|
37
|
+
return unless condition.is_a?(Hash)
|
38
|
+
return unless condition["useMeCabFilter"]
|
39
|
+
query = condition["query"]
|
40
|
+
return if query.nil?
|
41
|
+
match_columns = condition["matchTo"]
|
42
|
+
return unless match_columns.is_a?(Array)
|
43
|
+
return if match_columns.size != 1
|
44
|
+
match_column = match_columns.first
|
45
|
+
|
46
|
+
tokenizer = MeCabTokenizer.new
|
47
|
+
|
48
|
+
@records.open_cursor do |cursor|
|
49
|
+
count = 0
|
50
|
+
cursor.each do |record|
|
51
|
+
match_target = record[match_column]
|
52
|
+
body_terms = tokenizer.tokenize(match_target)
|
53
|
+
unless body_terms.include?(query)
|
54
|
+
record.delete
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
alias_method :original_apply_condition!, :apply_condition!
|
61
|
+
def apply_condition!(condition)
|
62
|
+
original_apply_condition!(condition)
|
63
|
+
apply_mecab_filter(condition)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/droonga/session.rb
CHANGED
@@ -48,10 +48,10 @@ module Droonga
|
|
48
48
|
end
|
49
49
|
tasks.each do |task|
|
50
50
|
task["n_of_inputs"] += 1
|
51
|
-
|
52
|
-
type =
|
51
|
+
step = task["step"]
|
52
|
+
type = step["type"]
|
53
53
|
command = "collector_" + type
|
54
|
-
n_of_expects =
|
54
|
+
n_of_expects = step["n_of_expects"]
|
55
55
|
message = {
|
56
56
|
"task"=>task,
|
57
57
|
"name"=>name,
|
@@ -61,9 +61,9 @@ module Droonga
|
|
61
61
|
return if task["n_of_inputs"] < n_of_expects
|
62
62
|
#the task is done
|
63
63
|
result = task["values"]
|
64
|
-
post =
|
64
|
+
post = step["post"]
|
65
65
|
@dispatcher.reply("body" => result) if post
|
66
|
-
|
66
|
+
step["descendants"].each do |name, routes|
|
67
67
|
message = {
|
68
68
|
"id" => @id,
|
69
69
|
"input" => name,
|
data/lib/droonga/test.rb
CHANGED
@@ -15,7 +15,7 @@
|
|
15
15
|
# License along with this library; if not, write to the Free Software
|
16
16
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
17
17
|
|
18
|
-
require "droonga/test/
|
18
|
+
require "droonga/test/stub_planner"
|
19
19
|
require "droonga/test/stub_handler"
|
20
20
|
require "droonga/test/stub_handler_message"
|
21
21
|
require "droonga/test/stub_handler_messenger"
|
data/lib/droonga/worker.rb
CHANGED
@@ -1,6 +1,4 @@
|
|
1
|
-
#
|
2
|
-
#
|
3
|
-
# Copyright (C) 2013 Droonga Project
|
1
|
+
# Copyright (C) 2013-2014 Droonga Project
|
4
2
|
#
|
5
3
|
# This library is free software; you can redistribute it and/or
|
6
4
|
# modify it under the terms of the GNU Lesser General Public
|
@@ -16,14 +14,15 @@
|
|
16
14
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
17
15
|
|
18
16
|
require "droonga/event_loop"
|
19
|
-
require "droonga/
|
17
|
+
require "droonga/handler_runner"
|
20
18
|
require "droonga/message_receiver"
|
21
19
|
|
22
20
|
module Droonga
|
23
21
|
module Worker
|
24
22
|
def initialize
|
25
23
|
@loop = EventLoop.new
|
26
|
-
@
|
24
|
+
@handler_runner = HandlerRunner.new(@loop,
|
25
|
+
config.merge(:dispatcher => nil))
|
27
26
|
receiver_socket = config[:message_receiver]
|
28
27
|
@message_receiver = MessageReceiver.new(@loop, receiver_socket) do |message|
|
29
28
|
process(message)
|
@@ -32,10 +31,10 @@ module Droonga
|
|
32
31
|
|
33
32
|
def run
|
34
33
|
$log.trace("#{log_tag}: run: start")
|
35
|
-
@
|
34
|
+
@handler_runner.start
|
36
35
|
@message_receiver.start
|
37
36
|
@loop.run
|
38
|
-
@
|
37
|
+
@handler_runner.shutdown
|
39
38
|
$log.trace("#{log_tag}: run: done")
|
40
39
|
end
|
41
40
|
|
@@ -49,7 +48,7 @@ module Droonga
|
|
49
48
|
private
|
50
49
|
def process(message)
|
51
50
|
$log.trace("#{log_tag}: process: start")
|
52
|
-
@
|
51
|
+
@handler_runner.process(message)
|
53
52
|
$log.trace("#{log_tag}: process: done")
|
54
53
|
end
|
55
54
|
|
data/sample/cluster/catalog.json
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
{
|
2
|
+
"version": 1,
|
2
3
|
"effective_date": "2013-09-01T00:00:00Z",
|
3
4
|
"zones": ["localhost:23003/droonga"],
|
4
5
|
"farms": {
|
@@ -10,7 +11,7 @@
|
|
10
11
|
"datasets": {
|
11
12
|
"Droonga": {
|
12
13
|
"workers": 2,
|
13
|
-
"plugins": ["
|
14
|
+
"plugins": ["groonga", "search", "crud"],
|
14
15
|
"number_of_replicas": 2,
|
15
16
|
"number_of_partitions": 2,
|
16
17
|
"partition_key": "_key",
|
@@ -36,8 +37,5 @@
|
|
36
37
|
}
|
37
38
|
}
|
38
39
|
}
|
39
|
-
},
|
40
|
-
"options": {
|
41
|
-
"plugins": ["groonga"]
|
42
40
|
}
|
43
41
|
}
|
@@ -0,0 +1,7 @@
|
|
1
|
+
table_create Memo TABLE_HASH_KEY ShortText
|
2
|
+
column_create Memo content COLUMN_SCALAR ShortText
|
3
|
+
|
4
|
+
table_create Terms TABLE_PAT_KEY ShortText \
|
5
|
+
--default_tokenizer TokenBigram \
|
6
|
+
--normalizer NormalizerAuto
|
7
|
+
column_create Terms memo_context COLUMN_INDEX|WITH_POSITION Memo content
|
@@ -0,0 +1,21 @@
|
|
1
|
+
{
|
2
|
+
"type": "search",
|
3
|
+
"dataset": "Droonga",
|
4
|
+
"body": {
|
5
|
+
"queries": {
|
6
|
+
"search": {
|
7
|
+
"source": "Memo",
|
8
|
+
"condition": {
|
9
|
+
"matchTo": ["content"],
|
10
|
+
"query": "パン",
|
11
|
+
"useMeCabFilter": true
|
12
|
+
},
|
13
|
+
"output": {
|
14
|
+
"elements": ["count", "records"],
|
15
|
+
"attributes": ["_key", "content"],
|
16
|
+
"limit": 10
|
17
|
+
}
|
18
|
+
}
|
19
|
+
}
|
20
|
+
}
|
21
|
+
}
|
@@ -0,0 +1,21 @@
|
|
1
|
+
{
|
2
|
+
"type": "search",
|
3
|
+
"dataset": "Droonga",
|
4
|
+
"body": {
|
5
|
+
"queries": {
|
6
|
+
"search": {
|
7
|
+
"source": "Memo",
|
8
|
+
"condition": {
|
9
|
+
"matchTo": ["content"],
|
10
|
+
"query": "パン",
|
11
|
+
"useMeCabFilter": false
|
12
|
+
},
|
13
|
+
"output": {
|
14
|
+
"elements": ["count", "records"],
|
15
|
+
"attributes": ["_key", "content"],
|
16
|
+
"limit": 10
|
17
|
+
}
|
18
|
+
}
|
19
|
+
}
|
20
|
+
}
|
21
|
+
}
|
@@ -10,7 +10,7 @@
|
|
10
10
|
"datasets": {
|
11
11
|
"Droonga": {
|
12
12
|
"workers": 2,
|
13
|
-
"plugins": ["
|
13
|
+
"plugins": ["groonga", "crud", "search"],
|
14
14
|
"number_of_replicas": 2,
|
15
15
|
"number_of_partitions": 2,
|
16
16
|
"partition_key": "_key",
|
@@ -38,7 +38,7 @@
|
|
38
38
|
},
|
39
39
|
"Watch": {
|
40
40
|
"workers": 2,
|
41
|
-
"plugins": ["
|
41
|
+
"plugins": ["groonga", "watch", "search", "crud"],
|
42
42
|
"number_of_replicas": 1,
|
43
43
|
"number_of_partitions": 1,
|
44
44
|
"partition_key": "_key",
|
@@ -54,8 +54,5 @@
|
|
54
54
|
}
|
55
55
|
}
|
56
56
|
}
|
57
|
-
},
|
58
|
-
"options": {
|
59
|
-
"plugins": ["crud", "groonga"]
|
60
57
|
}
|
61
58
|
}
|
@@ -13,6 +13,32 @@
|
|
13
13
|
"source": "unknown"
|
14
14
|
}
|
15
15
|
}
|
16
|
+
},
|
17
|
+
"errors": {
|
18
|
+
"sources0": {
|
19
|
+
"statusCode": 404,
|
20
|
+
"body": {
|
21
|
+
"name": "UnknownSource",
|
22
|
+
"message": "The source \"unknown\" does not exist. It must be a name of an existing table or another query.",
|
23
|
+
"detail": {
|
24
|
+
"unknown-source": {
|
25
|
+
"source": "unknown"
|
26
|
+
}
|
27
|
+
}
|
28
|
+
}
|
29
|
+
},
|
30
|
+
"sources1": {
|
31
|
+
"statusCode": 404,
|
32
|
+
"body": {
|
33
|
+
"name": "UnknownSource",
|
34
|
+
"message": "The source \"unknown\" does not exist. It must be a name of an existing table or another query.",
|
35
|
+
"detail": {
|
36
|
+
"unknown-source": {
|
37
|
+
"source": "unknown"
|
38
|
+
}
|
39
|
+
}
|
40
|
+
}
|
41
|
+
}
|
16
42
|
}
|
17
43
|
}
|
18
44
|
]
|