-
Notifications
You must be signed in to change notification settings - Fork 2
Couchdb2 mango query interface #128
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
seancookr
wants to merge
69
commits into
couchdb2
Choose a base branch
from
couchdb2-mango-query-interface
base: couchdb2
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
69 commits
Select commit
Hold shift + click to select a range
a7e8e4b
WIP on mango queries for couch2
870e1a9
Merge branch 'couchdb2' into couchdb2-mango-query-interface
470afe3
more WIP
9939246
WIP
fffd313
update test
c7e5002
change implementation to make scopes chainable
c17b026
update tests
2aa8c58
update doc
a1859be
pass query object amongst scope tree
9bb1007
update code wip
cc4701b
try to bind query object as self
f9e2619
composed query object
3cff807
passing tests
893703e
format tests better
952e422
update tests
925d3eb
update
f9152b5
passing tests
9997a31
clean up code
a3464a6
fix test for travis
37cd35e
change args so as not to conflict with method missing args
5d2b941
add more tests to flesh out the api
4c8a7f0
more dynamic test
58db538
respond to comments, add some tests, and refactor
c9f330e
remove some additional changes
dfbfb3d
add a autovivifying hash to simplify query building methods
b6490c4
better defined tests
09b1746
change macro method to mango_scope from scope
42fa49c
add more combination selectors
87aa9bf
fix syntax error
179469d
reorganize mango query class with a selector module
d3c8400
add more selectors to the selector table
2701fd3
add more selectors to the selector table
4c7ca06
add more selector options
f91d4a9
add more tests and more selectors
7a8da57
Merge branch 'couchdb2' into couchdb2-mango-query-interface
45f7c39
add args to lambda in selector_operator_map
ccfcb42
inline variable assignment in dolly scope initialize
c03f4cc
DRY up selector query creation
73c47f0
add skip method to mango query
f41e3ee
add more of the selector api by adding more operators and compiste se…
caabc19
add modulus and regex oeprators
e173079
add operator value type checking in order to aid developers
d6a6b8a
make select operator map private
d1e544e
create an object for operator and value type validation
e865c8c
allow for one off scopes to be chained to defined scopes, and vice versa
0b334d0
improve and DRY anonymous scope chaining using method missing
b0cbe0d
reorganize with module namespacing
6a3d8b7
allow for anonymous selectors on the class
04b96e4
more PIE variable names
fdb9625
clean up namespace
45ad32c
change lambda style
59ecaf6
add accepted value tests to query validator test
177fd28
add accepted value tests to query validator test
2da6307
move operator validation to proper place
b302059
get tests to pass
b8f2e2a
add more tests for unacceptable values
7bd26d7
update
f9a64a5
update
4e3e4d0
update
efe016c
use variable
0aeb3f7
inspect collection
02ec1b6
return a collection with mango scopes
dbe9e4e
return a collection with mango scopes
9434871
properly return http response for collection
4a18549
add a way to parse the response from mango, which does not include rows
a8c24b9
use parsed response
a70c133
change build_collection as well
fcd171b
use parsed for all
javierg cf41c44
Merge branch 'couchdb2-mango-query-interface' of github.com:amco/doll…
javierg File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| require 'dolly/mango/query_validator' | ||
| require 'dolly/mango/selector' | ||
| require 'dolly/mango/query' | ||
| require 'dolly/mango/scope' | ||
|
|
||
| module Dolly | ||
| module Mango | ||
|
|
||
| end | ||
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| module Dolly | ||
| module Mango | ||
| class Query | ||
| include Dolly::Mango::Selector | ||
|
|
||
| FIELDS_KEY = 'fields'.freeze | ||
| LIMIT_KEY = 'limit'.freeze | ||
| SKIP_KEY = 'skip'.freeze | ||
| SORT_KEY = 'sort'.freeze | ||
|
|
||
| attr_reader :proxy_class, :query | ||
|
|
||
| def initialize proxy_class | ||
| @proxy_class = proxy_class | ||
| @query = Hash.new{ |h,k| h[k] = Hash.new(&h.default_proc) } | ||
| query.compare_by_identity | ||
| end | ||
|
|
||
| def limit value | ||
| query[LIMIT_KEY] = value | ||
| end | ||
|
|
||
| def sort name, operator | ||
| query[SORT_KEY] ||= [] | ||
| query[SORT_KEY] << {name => operator} | ||
| end | ||
|
|
||
| def fields *fields | ||
| query[FIELDS_KEY] ||= [] | ||
| query[FIELDS_KEY].push *fields | ||
| end | ||
|
|
||
| def skip integer | ||
| query[SKIP_KEY] = integer.to_i | ||
| end | ||
| end | ||
| end | ||
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| module Dolly | ||
| module Mango | ||
| class QueryValidator | ||
|
|
||
| def initialize operator, value | ||
| @operator, @value = operator, value | ||
| end | ||
|
|
||
| def validate! | ||
| return if Selector::EQUALITY_OPERATORS.include? operator | ||
| raise Dolly::BadQueryArguement.new(operator, 'Boolean') if boolean_op? | ||
| raise Dolly::BadQueryArguement.new(operator, Selector::POSSIBLE_TYPE_VALUES.join(', ')) if type_op? | ||
| raise Dolly::BadQueryArguement.new(operator, Array) if array_op? | ||
| raise Dolly::BadQueryArguement.new(operator, Integer) if int_op? | ||
| raise Dolly::BadQueryArguement.new(operator, '[Divisor, Remainder] Array of Integers') if mod_op? | ||
| raise Dolly::BadQueryArguement.new(operator, String) if regex_op? | ||
| raise Dolly::BadQueryArguement.new(operator, Array) if combination_op? | ||
| end | ||
|
|
||
| private | ||
| attr_reader :operator, :value | ||
|
|
||
| def boolean_op? | ||
| operator == Selector::EXISTS_OPERATOR && ![true, false].include?(value) | ||
| end | ||
|
|
||
| def type_op? | ||
| operator == Selector::TYPE_OPERATOR && !Selector::POSSIBLE_TYPE_VALUES.include?(value) | ||
| end | ||
|
|
||
| def array_op? | ||
| Selector::ARRAY_OPERATORS.include?(operator) && !value.is_a?(Array) | ||
| end | ||
|
|
||
| def int_op? | ||
| operator == Selector::SIZE_OPERATOR && !value.is_a?(Integer) | ||
| end | ||
|
|
||
| def mod_op? | ||
| operator == Selector::MOD_OPERATOR && (!value.is_a?(Array) || value.count != 2 || value.none? {|el| el.is_a? Integer }) | ||
| end | ||
|
|
||
| def regex_op? | ||
| operator == Selector::REGEX_OPERATOR && !value.is_a?(String) | ||
| end | ||
|
|
||
| def combination_op? | ||
| Selector::COMBINATION_OPERATORS.include?(operator) && !value.is_a?(Array) | ||
| end | ||
| end | ||
| end | ||
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| module Dolly | ||
| module Mango | ||
| class Scope | ||
| attr_reader :query_object, :scope, :scope_args | ||
|
|
||
| delegate :proxy_class, :query, to: :query_object | ||
|
|
||
| def initialize query_object, scope, scope_args | ||
| @query_object, @scope, @scope_args = query_object, scope, scope_args | ||
| evaluate_scope | ||
| end | ||
|
|
||
| def method_missing(method, *args, &block) | ||
| if proxy_class.mango_scopes.include?(method) | ||
| proxy_class.mango_scopes[method].call(query_object, args) | ||
| elsif query_object.respond_to? method | ||
| query_object.send(method, *args, &block) | ||
| return self | ||
| else | ||
| resp = proxy_class.database.mango query.to_json | ||
| collection = Dolly::Collection.new(resp.response.body, proxy_class) | ||
| collection.send(method, *args, &block) | ||
| end | ||
| end | ||
|
|
||
| private | ||
|
|
||
| def evaluate_scope | ||
| query_object.instance_exec(*scope_args, &scope) | ||
| end | ||
| end | ||
| end | ||
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,125 @@ | ||
| module Dolly | ||
| module Mango | ||
| module Selector | ||
|
|
||
| SELECTOR = 'selector'.freeze | ||
|
|
||
| # Equality Operators | ||
| EQ_OPERATOR = '$eq'.freeze | ||
| NE_OPERATOR = '$ne'.freeze | ||
| GT_OPERATOR = '$gt'.freeze | ||
| LT_OPERATOR = '$lt'.freeze | ||
| GTE_OPERATOR = '$gte'.freeze | ||
| LTE_OPERATOR = '$lte'.freeze | ||
|
|
||
| EQUALITY_OPERATORS = [ EQ_OPERATOR, NE_OPERATOR, GT_OPERATOR, LT_OPERATOR, GTE_OPERATOR, LTE_OPERATOR ].freeze | ||
|
|
||
| # Object Operators | ||
| EXISTS_OPERATOR = '$exists'.freeze | ||
| TYPE_OPERATOR = '$type'.freeze | ||
|
|
||
| OBJECT_OPERATORS = [EXISTS_OPERATOR, TYPE_OPERATOR].freeze | ||
|
|
||
| # Array Operators | ||
| IN_OPERATOR = '$in'.freeze | ||
| NIN_OPERATOR = '$nin'.freeze | ||
| SIZE_OPERATOR = '$size'.freeze | ||
|
|
||
| ARRAY_OPERATORS = [IN_OPERATOR, NIN_OPERATOR].freeze | ||
|
|
||
| # Miscellaneous Operators | ||
| MOD_OPERATOR = '$mod'.freeze | ||
| REGEX_OPERATOR = '$regex'.freeze | ||
|
|
||
| MISC_OPERATORS = [MOD_OPERATOR, REGEX_OPERATOR].freeze | ||
|
|
||
| # Combination Operators | ||
| OR_OPERATOR = '$or'.freeze | ||
| NOT_SELECTOR = '$not'.freeze | ||
| NOR_SELECTOR = '$nor'.freeze | ||
| AND_OPERATOR = '$and'.freeze | ||
| ALL_OPERATOR = '$all'.freeze | ||
| EM_OPERATOR = '$elemMatch'.freeze | ||
|
|
||
| COMBINATION_OPERATORS = [ OR_OPERATOR, NOT_SELECTOR, NOR_SELECTOR, AND_OPERATOR, ALL_OPERATOR, EM_OPERATOR ].freeze | ||
|
|
||
| POSSIBLE_TYPE_VALUES = %w/null boolean number string array object/.freeze | ||
|
|
||
| ALL_OPERATORS = [ EQUALITY_OPERATORS, OBJECT_OPERATORS, ARRAY_OPERATORS, SIZE_OPERATOR, MISC_OPERATORS, COMBINATION_OPERATORS ].flatten.freeze | ||
|
|
||
| def selector name, *operator, value | ||
| proxy_operator = operator.last | ||
| operator_check! proxy_operator | ||
| operator = operator.count > 1 ? operator : operator.first | ||
| select_operator_map[operator].call(name, value) | ||
| end | ||
|
|
||
| private | ||
|
|
||
| def select_operator_map | ||
| { | ||
| eq: ->(name, value) { build_equality_selector name, value, EQ_OPERATOR }, | ||
| ne: ->(name, value) { build_equality_selector name, value, NE_OPERATOR }, | ||
| gt: ->(name, value) { build_equality_selector name, value, GT_OPERATOR }, | ||
| gte: ->(name, value) { build_equality_selector name, value, GTE_OPERATOR }, | ||
| lt: ->(name, value) { build_equality_selector name, value, LT_OPERATOR }, | ||
| lte: ->(name, value) { build_equality_selector name, value, LTE_OPERATOR }, | ||
|
|
||
| exists: ->(name, value=true) { build_equality_selector name, value, EXISTS_OPERATOR }, | ||
| type: ->(name, value) { build_equality_selector name, value, TYPE_OPERATOR }, | ||
|
|
||
| in: ->(name, value) { build_equality_selector name, value, IN_OPERATOR }, | ||
| nin: ->(name, value) { build_equality_selector name, value, NIN_OPERATOR }, | ||
| size: ->(name, value) { build_equality_selector name, value, SIZE_OPERATOR }, | ||
|
|
||
| mod: ->(name, value) { build_equality_selector name, value, MOD_OPERATOR }, | ||
| regex: ->(name, value) { build_equality_selector name, value, REGEX_OPERATOR }, | ||
|
|
||
| nor: ->(name, value) { build_exclusive_selector name, value, NOR_SELECTOR} , | ||
| all: ->(name, value) { build_exclusive_selector name, value, ALL_OPERATOR }, | ||
| and: ->(name, value) { build_exclusive_selector name, value, AND_OPERATOR }, | ||
| or: ->(name, value) { build_exclusive_selector name, value, OR_OPERATOR }, | ||
|
|
||
| [:em, :gt] => ->(name, value) { build_composite_selector name, value, EM_OPERATOR, GT_OPERATOR }, | ||
| [:em, :gte] => ->(name, value) { build_composite_selector name, value, EM_OPERATOR, GTE_OPERATOR }, | ||
| [:em, :lt] => ->(name, value) { build_composite_selector name, value, EM_OPERATOR, LT_OPERATOR }, | ||
| [:em, :lte] => ->(name, value) { build_composite_selector name, value, EM_OPERATOR, LTE_OPERATOR }, | ||
| [:em, :or] => ->(name, value) { build_composite_selector name, value, EM_OPERATOR, OR_OPERATOR }, | ||
| [:em, :and] => ->(name, value) { build_composite_selector name, value, EM_OPERATOR, AND_OPERATOR }, | ||
|
|
||
| [:not, :gt] => ->(name, value) { build_composite_selector name, value, NOT_OPERATOR, GT_OPERATOR }, | ||
| [:not, :gte] => ->(name, value) { build_composite_selector name, value, NOT_OPERATOR, GTE_OPERATOR }, | ||
| [:not, :lt] => ->(name, value) { build_composite_selector name, value, NOT_OPERATOR, LT_OPERATOR }, | ||
| [:not, :lte] => ->(name, value) { build_composite_selector name, value, NOT_OPERATOR, LTE_OPERATOR }, | ||
| [:not, :or] => ->(name, value) { build_composite_selector name, value, NOT_OPERATOR, OR_OPERATOR }, | ||
| [:not, :and] => ->(name, value) { build_composite_selector name, value, NOT_OPERATOR, AND_OPERATOR } | ||
| }.freeze | ||
| end | ||
|
|
||
| def build_equality_selector name, value, operator | ||
| operator_value_type_check!(operator, value) | ||
| query[SELECTOR][name][operator] = value | ||
| end | ||
|
|
||
| def build_exclusive_selector name, value, operator | ||
| operator_value_type_check!(operator, value) | ||
| query[SELECTOR][operator][name] = value | ||
| end | ||
|
|
||
| def build_composite_selector name, value, *operators | ||
| first, second = operators | ||
| operator_value_type_check!(second, value) | ||
|
|
||
| query[SELECTOR][name][first][second] = value | ||
| end | ||
|
|
||
| def operator_check! operator | ||
| raise Dolly::UnrecognizedOperator.new(operator) unless select_operator_map.keys.include? operator | ||
| end | ||
|
|
||
| def operator_value_type_check! operator, value | ||
| QueryValidator.new(operator, value).validate! | ||
| end | ||
| end | ||
| end | ||
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do you want to return the parsed response?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I dont see why I would. none of the other request do so
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👌 just wondering