akiva 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +15 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +2 -0
  5. data/Gemfile.lock +80 -0
  6. data/Guardfile +11 -0
  7. data/LICENSE +9 -0
  8. data/README.md +131 -0
  9. data/Rakefile +5 -0
  10. data/akiva.gemspec +38 -0
  11. data/lib/akiva.rb +11 -0
  12. data/lib/akiva/brain.rb +47 -0
  13. data/lib/akiva/cli.rb +58 -0
  14. data/lib/akiva/core_brain/actions/get_answers_from_subjects_and_properties.rb +73 -0
  15. data/lib/akiva/core_brain/actions/preparators/compare_two_subjects_properties_from_comparison_adjective.rb +19 -0
  16. data/lib/akiva/core_brain/actions/preparators/extract_property_from_comparison_adjective.rb +10 -0
  17. data/lib/akiva/core_brain/actions/preparators/regroup_separated_subjects.rb +10 -0
  18. data/lib/akiva/core_brain/actions/preparators/sort_answers_by_units.rb +35 -0
  19. data/lib/akiva/core_brain/actions/template.rb +23 -0
  20. data/lib/akiva/core_brain/filters/get_answers_from_subjects_and_properties.rb +15 -0
  21. data/lib/akiva/core_brain/filters/template.rb +9 -0
  22. data/lib/akiva/core_brain/formatters/boolean_from_comparison.rb +17 -0
  23. data/lib/akiva/core_brain/formatters/list_all_answers.rb +28 -0
  24. data/lib/akiva/core_brain/formatters/template.rb +19 -0
  25. data/lib/akiva/core_brain/helpers/comparison_adjectives_to_properties.rb +151 -0
  26. data/lib/akiva/core_brain/helpers/misc.rb +28 -0
  27. data/lib/akiva/core_brain/helpers/units.rb +167 -0
  28. data/lib/akiva/core_brain/loader.rb +3 -0
  29. data/lib/akiva/question.rb +88 -0
  30. data/lib/akiva/version.rb +9 -0
  31. data/spec/lib/akiva/brain_spec.rb +89 -0
  32. data/spec/lib/akiva/core_brain/actions/get_answers_from_subjects_and_properties_spec.rb +77 -0
  33. data/spec/lib/akiva/core_brain/actions/preparators/compare_two_subjects_properties_from_comparison_adjective_spec.rb +22 -0
  34. data/spec/lib/akiva/core_brain/actions/preparators/extract_property_from_comparison_adjective_spec.rb +11 -0
  35. data/spec/lib/akiva/core_brain/actions/preparators/regroup_separated_subjects_spec.rb +11 -0
  36. data/spec/lib/akiva/core_brain/actions/preparators/sort_answers_by_units_spec.rb +36 -0
  37. data/spec/lib/akiva/core_brain/filters/get_answers_from_subjects_and_properties_spec.rb +44 -0
  38. data/spec/lib/akiva/core_brain/formatters/boolean_from_comparison_spec.rb +38 -0
  39. data/spec/lib/akiva/core_brain/formatters/list_all_answers_spec.rb +60 -0
  40. data/spec/lib/akiva/core_brain/helpers/misc_spec.rb +26 -0
  41. data/spec/lib/akiva/core_brain/helpers/units_spec.rb +141 -0
  42. data/spec/lib/akiva/core_brain/question_spec.rb +170 -0
  43. data/spec/spec_helper.rb +37 -0
  44. data/spec/support/akiva_brain_helpers.rb +13 -0
  45. data/spec/support/akiva_statements_helpers.rb +66 -0
  46. metadata +75 -16
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 33bde0e703766c87d931ad9517446531f04c4727
4
- data.tar.gz: c1aae2abd0f533fe81ab71329bf31239e24df321
3
+ metadata.gz: 3b141693c56778ea3c49884ce12a4c4d9eb9d029
4
+ data.tar.gz: ca1146e56d4efee470fe4f9b8d35302e51860fa1
5
5
  SHA512:
6
- metadata.gz: c678181b3adf3a343e8e87f5527538b3f6f220225efb952b5f068affa912426a62cd26930a938401e8df2bc8edbecf53ab2479a8b93b41bd3d883fa3f71ca6fc
7
- data.tar.gz: 00bbd6ea2c6605c7328294df3a9b090f0bda590d8fc6473ba0ee5db3f8787543dc744f9a27435701cc4802dbe969d2f9ba494778c154880188e1305150daf5b8
6
+ metadata.gz: ea210219041b617487364ceb0c6dc2578f1e2d1a13720f548158d9469cb5eab8a622c703034f7e02beecdf3257482c7a3a6b40919fc1ccd9fb7589d30d64a90e
7
+ data.tar.gz: 44c5775d503401d30be0bea714943da61ed0609a2bde759e767e80b80c073cee6119aa60712e35fa3beb914c2ce83effaca70a7241455bd4cd1adc5db70efabd
@@ -0,0 +1,15 @@
1
+ /.bundle
2
+
3
+ *;*
4
+ .backups
5
+ .DS_Store
6
+ log/*
7
+ ._*
8
+ .__*
9
+ *~
10
+ *.swp
11
+ .rvmrc
12
+ .versions.conf
13
+ .ruby-version
14
+ *.gem
15
+ tmp/*
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "https://rubygems.org"
2
+ gemspec
@@ -0,0 +1,80 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ akiva (0.1.1)
5
+ awesome_print (~> 1.2)
6
+ rack (~> 1.4)
7
+ ruby-units (~> 1.4)
8
+ thebigdb (~> 1.3)
9
+ thor (~> 0.18)
10
+
11
+ GEM
12
+ remote: https://rubygems.org/
13
+ specs:
14
+ addressable (2.3.5)
15
+ awesome_print (1.2.0)
16
+ celluloid (0.15.2)
17
+ timers (~> 1.1.0)
18
+ coderay (1.1.0)
19
+ crack (0.4.1)
20
+ safe_yaml (~> 0.9.0)
21
+ diff-lcs (1.2.5)
22
+ ffi (1.9.3)
23
+ formatador (0.2.4)
24
+ guard (2.2.5)
25
+ formatador (>= 0.2.4)
26
+ listen (~> 2.1)
27
+ lumberjack (~> 1.0)
28
+ pry (>= 0.9.12)
29
+ thor (>= 0.18.1)
30
+ guard-rspec (2.6.0)
31
+ guard (>= 1.8)
32
+ rspec (~> 2.13)
33
+ listen (2.4.0)
34
+ celluloid (>= 0.15.2)
35
+ rb-fsevent (>= 0.9.3)
36
+ rb-inotify (>= 0.9)
37
+ lumberjack (1.0.4)
38
+ method_source (0.8.2)
39
+ pry (0.9.12.4)
40
+ coderay (~> 1.0)
41
+ method_source (~> 0.8)
42
+ slop (~> 3.4)
43
+ rack (1.5.2)
44
+ rake (10.1.1)
45
+ rb-fchange (0.0.6)
46
+ ffi
47
+ rb-fsevent (0.9.4)
48
+ rb-inotify (0.9.3)
49
+ ffi (>= 0.5.0)
50
+ rspec (2.14.1)
51
+ rspec-core (~> 2.14.0)
52
+ rspec-expectations (~> 2.14.0)
53
+ rspec-mocks (~> 2.14.0)
54
+ rspec-core (2.14.7)
55
+ rspec-expectations (2.14.4)
56
+ diff-lcs (>= 1.1.3, < 2.0)
57
+ rspec-mocks (2.14.4)
58
+ ruby-units (1.4.4)
59
+ safe_yaml (0.9.7)
60
+ slop (3.4.7)
61
+ thebigdb (1.4.1)
62
+ rack (~> 1.4)
63
+ thor (0.18.1)
64
+ timers (1.1.0)
65
+ webmock (1.16.1)
66
+ addressable (>= 2.2.7)
67
+ crack (>= 0.3.2)
68
+
69
+ PLATFORMS
70
+ ruby
71
+
72
+ DEPENDENCIES
73
+ akiva!
74
+ guard-rspec (~> 2.3)
75
+ rake (~> 10.0)
76
+ rb-fchange (~> 0)
77
+ rb-fsevent (~> 0)
78
+ rb-inotify (~> 0)
79
+ rspec (~> 2.12)
80
+ webmock (~> 1.9)
@@ -0,0 +1,11 @@
1
+ guard :rspec do
2
+ watch("spec/spec_helper.rb") { "spec" }
3
+ watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
4
+ watch("lib/akiva/question.rb") { "spec" }
5
+
6
+ watch(%r{^spec/.+_spec\.rb$})
7
+ watch(%r{^lib/akiva\.rb$}) { "spec" }
8
+ watch(%r{^lib/akiva/(.+)\.rb$}) do |m|
9
+ "spec/lib/akiva/#{m[1]}_spec.rb"
10
+ end
11
+ end
data/LICENSE ADDED
@@ -0,0 +1,9 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014, Christophe Maximin <christophe@thebigdb.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,131 @@
1
+ Akiva
2
+ =====
3
+
4
+ Akiva is a simple natural-language-processing, question-answering, artificial intelligence.
5
+
6
+ Its main role is to take a question, deconstruct it in order to query the
7
+ collaborative database of facts [TheBigDB](http://thebigdb.com), and format the answer into something readable for a human.
8
+
9
+ Akiva is obviously at a very early stage, and your contributions are more than welcome! See the "Contributing" section below for more info.
10
+
11
+ ## Why?
12
+
13
+ > ** What's the point? Isn't Google's Knowledge graph enough? What about Wolfram|Alpha? **
14
+ > -- you, right now
15
+
16
+ STORY TIME!
17
+ Back in 2005, while bored to death in a college class and daydreaming about how knowledge is structured, I asked myself why there wasn't anywhere I could ask questions in natural language. Because it would be pretty cool if there was.
18
+ I went back home, prototyped a little program doing just that. It sucked but it kinda worked, so I thought *"well, it isn't that hard, I guess geniuses everywhere are working on that, no need for me to spend more time on it. It surely will be available to everyone soon enough!"*. **Three years** passed: nothing.
19
+ The year 2009 saw the release of Wolfram|Alpha, which pretty much blew my mind: it was almost perfect. I guessed I just needed to wait a few more years and I can have all my trivial questions answered.
20
+ Fast forward **five more years**, and I'm sad to see this (as of January 6th, 2014):
21
+
22
+ [Google](http://imgur.com/RWjgcoM), [Wolfram|Apha](http://imgur.com/4QOHcN1), [Yahoo!](http://imgur.com/KXYnoqA) and [Bing](http://imgur.com/oOTloSQ) fail to answer the simple question « Is the iPhone 5s heavier than the Galaxy S4? » (click their names to see the results given);
23
+ Akiva answers "**No (iPhone 5s => 112 grams; Galaxy S4 => 130 grams)**". With only a few lines of code.
24
+
25
+ With the same few lines, Akiva can accurately smartly all the following questions:
26
+
27
+ * « What are the weight, width and height of an iPhone 5s and a Galaxy S4 ? »
28
+ * « What is the height requirement for Space Mountain ? »
29
+
30
+ … and much more.
31
+
32
+
33
+ **TL;DR: I truly love Google's Knowledge graph; I'm an eternal fan of Wolfram|Alpha, but I'm tired of waiting for search engines to implement a fully working simple question-answering engine.
34
+ Still, Akiva is not trying to be a real "*computational* knowledge engine" so it is NOT competing at all with Wolfram|Alpha.**
35
+
36
+ ## « It's nothing new!!! »
37
+
38
+ Maybe. What I, and you probably will, like about Akiva, is the fact that is always improving from two different angles:
39
+
40
+ * Akiva is open-source and a free software, so it can answer more and more types of questions with time
41
+ * Akiva's data source is the collaborative [TheBigDB](http://thebigdb.com), which makes it more knowledgable everyday
42
+
43
+ ## Usage
44
+
45
+ gem install akiva
46
+
47
+ You can then ask a question like this:
48
+
49
+ $ akiva ask "What's the weight of an iPhone 5s ?"
50
+
51
+ # Will output something like:
52
+ # 112 grams
53
+
54
+ Or you can ask a more complex question, like this one:
55
+
56
+ $ akiva ask "What are the weight, width and height of an iPhone 5s, a Nexus 5 and a Galaxy S4 ?"
57
+
58
+ # Will output something like:
59
+ # iPhone 5s => Weight: 112 grams, Width: 58.6 mm, Height: 123.8 mm
60
+ # Nexus 5 => Weight: 130 grams, Width: 69.17 mm, Height: 137.84 mm
61
+ # Galaxy S4 => Weight: 130 grams, Width: 58.6 mm, Height: 136.6 mm
62
+
63
+ In order to integrate it within an existing app, you can do something like this:
64
+
65
+ require "akiva"
66
+ puts Akiva::Question.new("What are the weight, width and height of an iPhone 5s, a Nexus 5 and a Galaxy S4 ?").formatted_response
67
+
68
+ Tip: If you're thinking about caching the requests made to TheBigDB, you should probably check out [the section "Caching" on its github page](https://github.com/thebigdb/thebigdb-ruby#caching).
69
+
70
+ ## Expanding
71
+
72
+ Akiva is quite easily expandable. Take for example this completely useless example, where Akiva would tell you what's the first word out of a list you provided.
73
+
74
+ require "akiva"
75
+
76
+ Akiva::Brain.update do
77
+ add_filter :get_first_word, /\AWhat's the first word of (?<words>.+?)\??\Z/i, formatter: :display_expected_word
78
+
79
+ add_action :get_first_word do |response|
80
+ response[:first_word] = response[:filter_captures]["words"].split(/ /).first
81
+ end
82
+
83
+ add_formatter :display_expected_word do |response|
84
+ response[:formatted] = "The word you're looking for is: « #{response[:first_word]} »"
85
+ end
86
+ end
87
+
88
+ # execution looks like:
89
+ # Akiva::Question.new("What's the first word of love is stronger than hate?").formatted_response
90
+ # => "The word you're looking for is: « love »"
91
+
92
+ For more info, you'll probably want to take a look at the templates for filters in [lib/akiva/core_brain/filters/template.rb](https://github.com/thebigdb/akiva/blob/master/lib/akiva/core_brain/filters/template.rb), actions in [lib/akiva/core_brain/actions/template.rb](https://github.com/thebigdb/akiva/blob/master/lib/akiva/core_brain/actions/template.rb) and formatters in [lib/akiva/core_brain/formatters/template.rb](https://github.com/thebigdb/akiva/blob/master/lib/akiva/core_brain/formatters/template.rb).
93
+ Also, don't hesitate to look around, see how existing filters/actions/formatters are built.
94
+
95
+
96
+ ## Contributing
97
+
98
+ You can run the tests with:
99
+
100
+ $ bundle install
101
+ $ bundle exec rspec spec/
102
+
103
+ As you can see, there are lots of types of questions Akiva can't answer yet (see [spec/lib/akiva/core_brain/question_spec.rb](https://github.com/thebigdb/akiva/blob/master/spec/lib/akiva/core_brain/question_spec.rb)). Just take one of them and make it work!
104
+
105
+ There are no crazy rules to contribute, just write good code, good tests, and send a pull request. It will really appreciated!
106
+
107
+ ## List of question types currently understood
108
+
109
+ This is a non-exhaustive list of question types Akiva can answer. Just remember that the fact that Akiva actually has the data to answer the question is entirely dependent on what is available in TheBigDB.
110
+ The words in parenthesis are variable.
111
+
112
+ What's (the weight) of (an iPhone 5s) ?
113
+ >>> 112 grams
114
+
115
+ What are (the weight and height) of (an iPhone 5s) ?
116
+ >>> Weight: 112 grams, Height: 123.8 mm
117
+
118
+ What are (the weight) of (an iPhone 5s and a Galaxy S4) ?
119
+ >>> iPhone 5s => 112 grams; Galaxy S4 => 130 grams
120
+
121
+ What are (the weight and height) of (an iPhone 5s and a Galaxy S4) ?
122
+ >>> iPhone 5s => Weight: 112 grams, Height: 123.8 mm
123
+ Galaxy S4 => Weight: 130 grams, Height: 136.6 mm
124
+
125
+ Is (the iPhone 5s) (heavier) than (the Galaxy S4) ?
126
+ >>> No (iPhone 5s => 112 grams; Galaxy S4 => 130 grams)
127
+
128
+
129
+ ## License
130
+
131
+ This software is distributed under the MIT License. Copyright (c) 2014, Christophe Maximin <christophe@thebigdb.com>
@@ -0,0 +1,5 @@
1
+ require 'rspec/core/rake_task'
2
+ RSpec::Core::RakeTask.new('spec')
3
+
4
+ desc "Run tests"
5
+ task :default => [:spec]
@@ -0,0 +1,38 @@
1
+ $:.push File.expand_path("../lib", __FILE__)
2
+ require "akiva/version"
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "akiva"
6
+ s.version = Akiva::VERSION::STRING
7
+ s.summary = "Simple natural language processing, question-answering artificial intelligence, based on TheBigDB"
8
+ s.description = "Akiva is a simple natural language processing, question-answering artificial intelligence. It is a demonstration of how to use the API of TheBigDB (http://thebigbdb.com) to create knowledgable software."
9
+ s.authors = ["Christophe Maximin"]
10
+ s.email = "christophe@thebigdb.com"
11
+ s.homepage = "https://github.com/thebigdb/akiva"
12
+ s.licenses = ['MIT']
13
+
14
+ s.platform = Gem::Platform::RUBY
15
+
16
+ s.add_runtime_dependency "rack", "~> 1.4"
17
+ s.add_runtime_dependency "ruby-units", "~> 1.4"
18
+ s.add_runtime_dependency "thebigdb", "~> 1.3"
19
+ s.add_runtime_dependency "thor", "~> 0.18"
20
+ # s.add_runtime_dependency "numbers_in_words", "~> 0.0", ">= 0.0.1"
21
+ s.add_runtime_dependency "awesome_print", "~> 1.2"
22
+
23
+ s.add_development_dependency "rake", "~> 10.0"
24
+ s.add_development_dependency "rspec", "~> 2.12"
25
+ s.add_development_dependency "guard-rspec", "~> 2.3"
26
+ s.add_development_dependency "rb-inotify", "~> 0"
27
+ s.add_development_dependency "rb-fsevent", "~> 0"
28
+ s.add_development_dependency "rb-fchange", "~> 0"
29
+ s.add_development_dependency "webmock", "~> 1.9"
30
+
31
+
32
+
33
+ s.files = `git ls-files`.split("\n")
34
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
35
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
36
+
37
+ s.require_paths = ["lib"]
38
+ end
@@ -0,0 +1,11 @@
1
+ require "thebigdb"
2
+ # require "numbers_in_words/duck_punch"
3
+ require "ruby-units"
4
+
5
+ %w(
6
+ brain
7
+ core_brain/loader
8
+ question
9
+ ).each do |file_name|
10
+ require File.join(File.dirname(__FILE__), "akiva", file_name)
11
+ end
@@ -0,0 +1,47 @@
1
+ module Akiva
2
+ module Brain
3
+ @@filters = []
4
+ @@actions = {}
5
+ @@formatters = {}
6
+
7
+ class << self
8
+ def update(&block)
9
+ instance_exec(&block)
10
+ end
11
+
12
+ def empty
13
+ @@filters = []
14
+ @@actions = {}
15
+ @@formatters = {}
16
+ end
17
+
18
+ def add_filter(action_name, regex, options = {})
19
+ @@filters.unshift({
20
+ regex: regex,
21
+ action: action_name
22
+ }.merge(options))
23
+ end
24
+
25
+ def add_action(action_name, class_instance = nil, &block)
26
+ @@actions[action_name] = class_instance || block
27
+ end
28
+
29
+ def add_formatter(formatter_name, class_instance = nil, &block)
30
+ @@formatters[formatter_name] = class_instance || block
31
+ end
32
+
33
+ # readers
34
+ def filters
35
+ @@filters
36
+ end
37
+
38
+ def actions
39
+ @@actions
40
+ end
41
+
42
+ def formatters
43
+ @@formatters
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,58 @@
1
+ require "thor"
2
+
3
+ module Akiva
4
+ class CLI < Thor
5
+
6
+ desc "ask QUESTION", "Process a question, and display the answer"
7
+ # method_option "verbose", type: :boolean, aliases: "-v", desc: "Displays the elements found in the question"
8
+ # option :verbose, type: :boolean, aliases: "-v"
9
+ option "verbose", type: :boolean, aliases: "-v", desc: "Displays the requests made to TheBigDB"
10
+ option "headers", type: :boolean, desc: "Displays headers of the requests made to TheBigDB (depends on --verbose/-v)", default: false
11
+ option "caller", type: :boolean, desc: "Displays stack trace from which each request made to TheBigDB is made (depends on --verbose/-v)", default: false
12
+ option "api-host", type: :string, desc: "Host to send requests for TheBigDB", default: "api.thebigdb.com"
13
+
14
+ def ask(question)
15
+ TheBigDB.raise_on_api_status_error = true
16
+
17
+ TheBigDB.api_host = options["api-host"]
18
+
19
+ if options["verbose"]
20
+ require "awesome_print"
21
+ TheBigDB.before_request_execution = lambda do |request|
22
+ puts "REQUEST DATA SENT:"
23
+ data_sent = request.data_sent
24
+ data_sent.delete("headers") unless options["headers"]
25
+ ap data_sent
26
+ if options["caller"]
27
+ puts "CALLED FROM:"
28
+ puts caller.join("\n")
29
+ end
30
+ puts
31
+ end
32
+
33
+ TheBigDB.after_request_execution = lambda do |request|
34
+ puts "REQUEST DATA RECEIVED:"
35
+ data_received = request.data_received
36
+ data_received.delete("headers") unless options["headers"]
37
+ ap data_received
38
+ puts
39
+ end
40
+ end
41
+
42
+ akiva_question = Akiva::Question.new(question)
43
+ begin
44
+ if response = akiva_question.formatted_response
45
+ puts response.gsub("; ", "\n")
46
+ else
47
+ puts "Sorry, Akiva can't answer that question for now."
48
+ end
49
+ rescue TheBigDB::Request::ApiStatusError
50
+ puts "Sorry, it looks like TheBigDB is currently unavailable."
51
+ end
52
+
53
+ end
54
+
55
+ end
56
+ end
57
+
58
+ Akiva::CLI.start
@@ -0,0 +1,73 @@
1
+ Akiva::Brain.update do
2
+
3
+ add_action :get_answers_from_subjects_and_properties do |response|
4
+
5
+ # It's possible we're arriving here with already defined subjects and properties, in that case, skip the default request
6
+ unless response[:defined_subjects] and response[:defined_properties]
7
+ regex_matched_subjects = response[:filter_captures]["subjects"]
8
+ regex_matched_properties = response[:filter_captures]["properties"]
9
+
10
+ # First, we try the obvious subject x property, works if there's only one subject and one property
11
+ search = TheBigDB.search(subject: regex_matched_subjects, property: regex_matched_properties).with(per_page: 1)
12
+
13
+ if search["total_results"] == 1
14
+ statement = search["statements"].first
15
+ response[:subjects] = [statement["nodes"]["subject"]]
16
+ response[:properties] = {statement["nodes"]["subject"] => [statement["nodes"]["property"]]}
17
+ response[:answers] = {statement["nodes"]["subject"] => {statement["nodes"]["property"] => statement["nodes"]["answer"]}}
18
+
19
+ next # we're happy with what we have, no need to continue the action.
20
+ end
21
+
22
+ # If we don't have an obvious answer, it may be because we're searching for multiple subjects and/or properties
23
+ degrouped_subjects = Akiva::Brain::Helpers.degroup_multiple_values(regex_matched_subjects)
24
+ degrouped_properties = Akiva::Brain::Helpers.degroup_multiple_values(regex_matched_properties)
25
+
26
+ if degrouped_subjects.size == 1 and degrouped_properties.size == 1
27
+ next # sadly we don't have anything to further analyze/search for, so we're exiting the action empty-handed.
28
+ end
29
+
30
+ # Let's make products of elements to be sure we're not missing anything
31
+ products_mega_groups = []
32
+ products_mega_groups << [regex_matched_subjects].product(degrouped_properties)
33
+ products_mega_groups << degrouped_subjects.product([regex_matched_properties])
34
+ products_mega_groups << degrouped_subjects.product(degrouped_properties)
35
+ products_mega_groups.uniq!
36
+ else
37
+ products_mega_groups = [response[:defined_subjects].product(response[:defined_properties])]
38
+ end
39
+
40
+ products_mega_groups.each do |product_mega_group|
41
+ statements_compiled = {}
42
+ statements_found = 0
43
+
44
+ product_mega_group.each do |group|
45
+ search = TheBigDB.search(subject: group[0], property: group[1]).with(per_page: 1)
46
+ if search["total_results"] == 1
47
+ statements_found += 1
48
+ statement = search["statements"].first
49
+ statements_compiled[group[0]] ||= {}
50
+ statements_compiled[group[0]][group[1]] = statement["nodes"]["answer"]
51
+ else
52
+ break # if one is missing, we exit this each block since we're looking for a perfect product results
53
+ end
54
+ end
55
+
56
+ if statements_found == product_mega_group.size
57
+ response[:subjects] = statements_compiled.keys
58
+ response[:properties] = {}
59
+ statements_compiled.each_pair do |k, v|
60
+ response[:properties][k] = v.keys
61
+ end
62
+ response[:answers] = statements_compiled
63
+
64
+ break # we're happy with what we have, no need to continue mega_groups each-block.
65
+ end
66
+ end
67
+
68
+
69
+
70
+
71
+ end
72
+
73
+ end