naranya_ecm-sdk 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +22 -0
- data/README.md +33 -0
- data/Rakefile +1 -0
- data/dev/create_cvm.rb +31 -0
- data/dev/init.rb +2 -0
- data/dev/load_cvm.rb +12 -0
- data/dev/search.rb +3 -0
- data/lib/naranya_ecm/behaviors/localizable.rb +41 -0
- data/lib/naranya_ecm/behaviors/timestampable.rb +22 -0
- data/lib/naranya_ecm/behaviors.rb +9 -0
- data/lib/naranya_ecm/cache/key.rb +73 -0
- data/lib/naranya_ecm/cache/methods.rb +78 -0
- data/lib/naranya_ecm/cache.rb +9 -0
- data/lib/naranya_ecm/has_many_patch.rb +105 -0
- data/lib/naranya_ecm/lifecycles/content_lifecycle.rb +36 -0
- data/lib/naranya_ecm/lifecycles/lifecycleable.rb +43 -0
- data/lib/naranya_ecm/lifecycles/version_lifecycle.rb +75 -0
- data/lib/naranya_ecm/lifecycles.rb +10 -0
- data/lib/naranya_ecm/models/category.rb +42 -0
- data/lib/naranya_ecm/models/content.rb +102 -0
- data/lib/naranya_ecm/models/content_version.rb +38 -0
- data/lib/naranya_ecm/models/download_authorization.rb +55 -0
- data/lib/naranya_ecm/models/lifecycle.rb +36 -0
- data/lib/naranya_ecm/models/media_resource.rb +33 -0
- data/lib/naranya_ecm/models.rb +13 -0
- data/lib/naranya_ecm/search/hit.rb +17 -0
- data/lib/naranya_ecm/search/methods.rb +26 -0
- data/lib/naranya_ecm/search/query.rb +296 -0
- data/lib/naranya_ecm/search/results.rb +161 -0
- data/lib/naranya_ecm/search.rb +14 -0
- data/lib/naranya_ecm/site_from_configuration.rb +15 -0
- data/lib/naranya_ecm-sdk/version.rb +3 -0
- data/lib/naranya_ecm-sdk.rb +84 -0
- data/naranya_ecm-sdk.gemspec +33 -0
- data/spec/models/category_spec.rb +11 -0
- data/spec/models/content_spec.rb +11 -0
- data/spec/models/content_version_spec.rb +7 -0
- data/spec/models/download_authorization.rb +7 -0
- data/spec/models/media_spec.rb +7 -0
- data/spec/models/module_spec.rb +18 -0
- data/spec/spec_helper.rb +47 -0
- data/spec/support/naranya_ecms_shared_specs.rb +33 -0
- metadata +236 -0
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'active_resource'
|
2
|
+
|
3
|
+
module NaranyaEcm
|
4
|
+
module Models
|
5
|
+
class Category < ::ActiveResource::Base
|
6
|
+
|
7
|
+
include SiteFromConfiguration
|
8
|
+
|
9
|
+
include NaranyaEcm::Behaviors::Timestampable
|
10
|
+
|
11
|
+
include NaranyaEcm::Search::Methods
|
12
|
+
include NaranyaEcm::Cache::Methods
|
13
|
+
|
14
|
+
class NameTranslations < ::ActiveResource::Base
|
15
|
+
include NaranyaEcm::Behaviors::Localizable
|
16
|
+
end
|
17
|
+
|
18
|
+
self.include_root_in_json = true
|
19
|
+
|
20
|
+
# Definir los atributos conocidos:
|
21
|
+
schema do
|
22
|
+
# define each attribute separately
|
23
|
+
string :id, :code, :created_at, :updated_at
|
24
|
+
|
25
|
+
# unsupported types should be left as strings
|
26
|
+
# overload the accessor methods if you need to convert them
|
27
|
+
#attribute 'created_at', 'string'
|
28
|
+
attribute 'name_translations', 'string'
|
29
|
+
attribute 'description_translations', 'string'
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
delegate :name, to: :name_translations
|
34
|
+
delegate :description, to: :title_translations
|
35
|
+
|
36
|
+
def contents
|
37
|
+
Content.find :all, params: { category_id: self.id }
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'active_resource'
|
2
|
+
|
3
|
+
module NaranyaEcm
|
4
|
+
module Models
|
5
|
+
class Content < ::ActiveResource::Base
|
6
|
+
|
7
|
+
include ActiveModel::Dirty
|
8
|
+
|
9
|
+
include SiteFromConfiguration
|
10
|
+
|
11
|
+
include NaranyaEcm::Search::Methods
|
12
|
+
include NaranyaEcm::Cache::Methods
|
13
|
+
|
14
|
+
#include HasManyPatch
|
15
|
+
|
16
|
+
class TitleTranslations < ::ActiveResource::Base
|
17
|
+
include NaranyaEcm::Behaviors::Localizable
|
18
|
+
end
|
19
|
+
|
20
|
+
class DescriptionTranslations < ::ActiveResource::Base
|
21
|
+
include NaranyaEcm::Behaviors::Localizable
|
22
|
+
end
|
23
|
+
|
24
|
+
class ContentRating < HashWithIndifferentAccess
|
25
|
+
def initialize(*args)
|
26
|
+
super()
|
27
|
+
given_attrs = args.shift
|
28
|
+
given_attrs.each { |k,v| self[k] = v } unless given_attrs.nil?
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
self.include_root_in_json = true
|
33
|
+
|
34
|
+
has_many :versions, class_name: "naranya_ecm/models/content_version"
|
35
|
+
|
36
|
+
has_one :current_version, class_name: "naranya_ecm/models/content_version"
|
37
|
+
belongs_to :category, class_name: "naranya_ecm/models/category"
|
38
|
+
|
39
|
+
# Definir los atributos conocidos:
|
40
|
+
schema do
|
41
|
+
# define each attribute separately
|
42
|
+
string :id, :type, :lifecycle_name, :lifecycle_state, :content_rating, :created_at, :updated_at
|
43
|
+
|
44
|
+
string :author
|
45
|
+
string :main_url
|
46
|
+
|
47
|
+
# unsupported types should be left as strings
|
48
|
+
# overload the accessor methods if you need to convert them
|
49
|
+
#attribute 'created_at', 'string'
|
50
|
+
attribute 'title_translations', 'string'
|
51
|
+
attribute 'description_translations', 'string'
|
52
|
+
attribute 'keywords', 'string'
|
53
|
+
|
54
|
+
attribute 'media_resources', 'string'
|
55
|
+
attribute 'current_version', 'string'
|
56
|
+
|
57
|
+
# Extra attributes:
|
58
|
+
NaranyaEcm.options['extra_attributes']['content'].each do |key, val|
|
59
|
+
attribute key, val
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
schema_attrs = self.schema.keys.map(&:to_sym)
|
65
|
+
define_attribute_methods(*schema_attrs)
|
66
|
+
schema_attrs.each do |attr_sym|
|
67
|
+
# getter:
|
68
|
+
define_method(attr_sym) { @attributes[attr_sym] }
|
69
|
+
# setter:
|
70
|
+
define_method("#{attr_sym}=".to_sym) do |value|
|
71
|
+
self.send "#{attr_sym}_will_change!".to_sym unless value == @attributes[attr_sym]
|
72
|
+
@attributes[attr_sym] = value
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
delegate :title, to: :title_translations
|
77
|
+
delegate :description, to: :description_translations
|
78
|
+
|
79
|
+
include NaranyaEcm::Behaviors::Timestampable
|
80
|
+
|
81
|
+
# include NaranyaEcm::Lifecycles::Lifecycleable
|
82
|
+
# ##################################################################
|
83
|
+
# # StateMachine:
|
84
|
+
# state_machine :lifecycle_state, initial: :draft do
|
85
|
+
|
86
|
+
# state :draft
|
87
|
+
|
88
|
+
# state :awaiting_validation
|
89
|
+
|
90
|
+
# state :rejected
|
91
|
+
|
92
|
+
# state :validated
|
93
|
+
|
94
|
+
# state :published
|
95
|
+
|
96
|
+
# state :deactivated
|
97
|
+
|
98
|
+
# end
|
99
|
+
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'active_resource'
|
2
|
+
|
3
|
+
module NaranyaEcm
|
4
|
+
module Models
|
5
|
+
class ContentVersion < ::ActiveResource::Base
|
6
|
+
|
7
|
+
include SiteFromConfiguration
|
8
|
+
include NaranyaEcm::Behaviors::Timestampable
|
9
|
+
|
10
|
+
self.include_root_in_json = true
|
11
|
+
|
12
|
+
# Definir los atributos conocidos:
|
13
|
+
schema do
|
14
|
+
# define each attribute separately
|
15
|
+
string :id, :name, :lifecycle_name, :lifecycle_state
|
16
|
+
|
17
|
+
integer :code
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
class DescriptionTranslations < ::ActiveResource::Base
|
22
|
+
include NaranyaEcm::Behaviors::Localizable
|
23
|
+
end
|
24
|
+
|
25
|
+
delegate :description, to: :description_translations
|
26
|
+
|
27
|
+
belongs_to :content,
|
28
|
+
class_name: "naranya_ecm/models/content"
|
29
|
+
|
30
|
+
has_many :media_resources,
|
31
|
+
class_name: "naranya_ecm/models/media_resource"
|
32
|
+
|
33
|
+
#include NaranyaEcm::Lifecycles::Lifecycleable
|
34
|
+
# include NaranyaEcm::Lifecycles::VersionLifecycle
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'active_resource'
|
2
|
+
|
3
|
+
module NaranyaEcm
|
4
|
+
module Models
|
5
|
+
class DownloadAuthorization < ActiveResource::Base
|
6
|
+
|
7
|
+
class Tokens < ::ActiveResource::Base
|
8
|
+
# Delegar el acceso tipo "Enumerable":
|
9
|
+
delegate :any?, to: :@attributes
|
10
|
+
|
11
|
+
# Re-establece el acceso tipo Hash:
|
12
|
+
delegate :[], to: :@attributes
|
13
|
+
delegate :[]=, to: :@attributes
|
14
|
+
self.site = "http://www.example.com"
|
15
|
+
|
16
|
+
def to_query_params
|
17
|
+
@attributes.map { |key, value| "#{key}=#{value}" }.join '&'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
include SiteFromConfiguration
|
22
|
+
include NaranyaEcm::Behaviors::Timestampable
|
23
|
+
|
24
|
+
self.include_root_in_json = true
|
25
|
+
|
26
|
+
# Definir los atributos conocidos:
|
27
|
+
schema do
|
28
|
+
string :id, :media_resource_id, :expires_at, :tokens, :created_at, :updated_at
|
29
|
+
end
|
30
|
+
|
31
|
+
belongs_to :media_resource, class_name: "naranya_ecm/models/media_resource"
|
32
|
+
|
33
|
+
def media_resource=(given_media_resource)
|
34
|
+
@attributes[:media_resource_id] = given_media_resource.id
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize(attributes = {}, persisted = false)
|
38
|
+
if given_media_resource = attributes.delete(:media_resource)
|
39
|
+
attributes[:media_resource_id] = given_media_resource.id
|
40
|
+
end
|
41
|
+
super
|
42
|
+
end
|
43
|
+
|
44
|
+
def authorized_url
|
45
|
+
unless @authorized_url
|
46
|
+
uri = URI(self.media_resource.downloadable_url)
|
47
|
+
uri.query = self.tokens.to_query_params
|
48
|
+
@authorized_url = uri.to_s
|
49
|
+
end
|
50
|
+
@authorized_url
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
|
2
|
+
module NaranyaEcm
|
3
|
+
module Models
|
4
|
+
|
5
|
+
# Value Object de Lifecycle de documentos / versiones / media / etc.
|
6
|
+
class Lifecycle < String
|
7
|
+
include Comparable
|
8
|
+
|
9
|
+
KNOWN_LIFECYCLES = %w(simple validatable)
|
10
|
+
.map { |m| m.to_sym }
|
11
|
+
.freeze
|
12
|
+
|
13
|
+
def is_lifecycle?(given_lifecycle)
|
14
|
+
given_lifecycle.to_s == self
|
15
|
+
end
|
16
|
+
|
17
|
+
KNOWN_LIFECYCLES.each do |lifecycle_name|
|
18
|
+
define_method("is_#{lifecycle_name}?") do
|
19
|
+
is_lifecycle?(lifecycle_name)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def method_missing(method_name, *args, &block)
|
24
|
+
if method_name.to_s =~ /^is_(\w+)\?$/
|
25
|
+
is_lifecycle?($1)
|
26
|
+
else
|
27
|
+
super # You *must* call super if you don't handle the
|
28
|
+
# method, otherwise you'll mess up Ruby's method
|
29
|
+
# lookup.
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'active_resource'
|
2
|
+
|
3
|
+
module NaranyaEcm
|
4
|
+
module Models
|
5
|
+
class MediaResource < ActiveResource::Base
|
6
|
+
|
7
|
+
include SiteFromConfiguration
|
8
|
+
include NaranyaEcm::Behaviors::Timestampable
|
9
|
+
|
10
|
+
self.include_root_in_json = true
|
11
|
+
|
12
|
+
# Definir los atributos conocidos:
|
13
|
+
schema do
|
14
|
+
# define each attribute separately
|
15
|
+
string :id, :access_type, :type, :role, :downloadable_url
|
16
|
+
|
17
|
+
string :author
|
18
|
+
string :main_url
|
19
|
+
|
20
|
+
# unsupported types should be left as strings
|
21
|
+
# overload the accessor methods if you need to convert them
|
22
|
+
#attribute 'created_at', 'string'
|
23
|
+
attribute 'created_at', 'string'
|
24
|
+
attribute 'updated_at', 'string'
|
25
|
+
attribute 'compatible_device_ids', 'string'
|
26
|
+
attribute 'compatible_device_oses', 'string'
|
27
|
+
attribute 'compatible_device_os_versions', 'string'
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'active_support/dependencies/autoload'
|
2
|
+
|
3
|
+
module NaranyaEcm
|
4
|
+
module Models
|
5
|
+
extend ActiveSupport::Autoload
|
6
|
+
autoload :Category
|
7
|
+
autoload :Content
|
8
|
+
autoload :ContentVersion
|
9
|
+
autoload :Lifecycle
|
10
|
+
autoload :DownloadAuthorization
|
11
|
+
autoload :MediaResource
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module NaranyaEcm
|
2
|
+
module Search
|
3
|
+
class Hit
|
4
|
+
attr_reader :id, :type, :updated_at, :order, :score
|
5
|
+
attr_accessor :cache_key
|
6
|
+
def initialize(attributes={})
|
7
|
+
@id = attributes.delete :_id
|
8
|
+
@type = attributes.delete :_type
|
9
|
+
@score = attributes.delete :_score
|
10
|
+
|
11
|
+
source = attributes.delete :_source
|
12
|
+
@updated_at = source.has_key?(:updated_at) ? source.delete(:updated_at).to_datetime : nil
|
13
|
+
@order = attributes.delete :order
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
module NaranyaEcm
|
3
|
+
module Search
|
4
|
+
module Methods
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
attr_reader :search_score, :search_order
|
9
|
+
end
|
10
|
+
|
11
|
+
def merge_search_hit_data(hit)
|
12
|
+
@search_score, @search_order = hit.score, hit.order
|
13
|
+
end
|
14
|
+
|
15
|
+
module ClassMethods
|
16
|
+
def search(*args)
|
17
|
+
Results.new(*args, self)
|
18
|
+
end
|
19
|
+
def kick_search(terms, options={})
|
20
|
+
search(Query.build_searchkick_like(terms, options))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,296 @@
|
|
1
|
+
module NaranyaEcm
|
2
|
+
module Search
|
3
|
+
class Query < Hash
|
4
|
+
class << self
|
5
|
+
def build_searchkick_like(term, options = {})
|
6
|
+
if term.is_a?(Hash)
|
7
|
+
options = term
|
8
|
+
term = nil
|
9
|
+
else
|
10
|
+
term = term.to_s
|
11
|
+
end
|
12
|
+
|
13
|
+
fields =
|
14
|
+
if options[:fields]
|
15
|
+
if options[:autocomplete]
|
16
|
+
options[:fields].map{|f| "#{f}.autocomplete" }
|
17
|
+
else
|
18
|
+
options[:fields].map do |value|
|
19
|
+
k, v = value.is_a?(Hash) ? value.to_a.first : [value, :word]
|
20
|
+
"#{k}.#{v == :word ? "analyzed" : v}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
else
|
24
|
+
["_all"]
|
25
|
+
end
|
26
|
+
|
27
|
+
operator = options[:partial] ? "or" : "and"
|
28
|
+
|
29
|
+
# model and eagar loading
|
30
|
+
load = options[:load].nil? ? true : options[:load]
|
31
|
+
load = (options[:include] ? {include: options[:include]} : true) if load
|
32
|
+
|
33
|
+
# pagination
|
34
|
+
page = [options[:page].to_i, 1].max
|
35
|
+
per_page = (options[:limit] || options[:per_page] || 10).to_i
|
36
|
+
offset = options[:offset] || (page - 1) * per_page
|
37
|
+
|
38
|
+
all = term == "*"
|
39
|
+
|
40
|
+
if options[:query]
|
41
|
+
payload = options[:query]
|
42
|
+
elsif options[:similar]
|
43
|
+
payload = {
|
44
|
+
more_like_this: {
|
45
|
+
fields: fields,
|
46
|
+
like_text: term,
|
47
|
+
min_doc_freq: 1,
|
48
|
+
min_term_freq: 1,
|
49
|
+
analyzer: "searchkick_search2"
|
50
|
+
}
|
51
|
+
}
|
52
|
+
elsif all
|
53
|
+
payload = {
|
54
|
+
match_all: {}
|
55
|
+
}
|
56
|
+
else
|
57
|
+
if options[:autocomplete]
|
58
|
+
payload = {
|
59
|
+
multi_match: {
|
60
|
+
fields: fields,
|
61
|
+
query: term,
|
62
|
+
analyzer: "searchkick_autocomplete_search"
|
63
|
+
}
|
64
|
+
}
|
65
|
+
else
|
66
|
+
queries = []
|
67
|
+
fields.each do |field|
|
68
|
+
if field == "_all" or field.end_with?(".analyzed")
|
69
|
+
shared_options = {
|
70
|
+
fields: [field],
|
71
|
+
query: term,
|
72
|
+
use_dis_max: false,
|
73
|
+
operator: operator,
|
74
|
+
cutoff_frequency: 0.001
|
75
|
+
}
|
76
|
+
queries.concat [
|
77
|
+
{multi_match: shared_options.merge(boost: 10, analyzer: "searchkick_search")},
|
78
|
+
{multi_match: shared_options.merge(boost: 10, analyzer: "searchkick_search2")}
|
79
|
+
]
|
80
|
+
if options[:misspellings] != false
|
81
|
+
distance = (options[:misspellings].is_a?(Hash) && options[:misspellings][:distance]) || 1
|
82
|
+
queries.concat [
|
83
|
+
{multi_match: shared_options.merge(fuzziness: distance, max_expansions: 3, analyzer: "searchkick_search")},
|
84
|
+
{multi_match: shared_options.merge(fuzziness: distance, max_expansions: 3, analyzer: "searchkick_search2")}
|
85
|
+
]
|
86
|
+
end
|
87
|
+
else
|
88
|
+
queries << {
|
89
|
+
multi_match: {
|
90
|
+
fields: [field],
|
91
|
+
query: term,
|
92
|
+
analyzer: "searchkick_autocomplete_search"
|
93
|
+
}
|
94
|
+
}
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
payload = {
|
99
|
+
dis_max: {
|
100
|
+
queries: queries
|
101
|
+
}
|
102
|
+
}
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
custom_filters = []
|
108
|
+
|
109
|
+
if options[:boost]
|
110
|
+
custom_filters << {
|
111
|
+
filter: {
|
112
|
+
exists: {
|
113
|
+
field: options[:boost]
|
114
|
+
}
|
115
|
+
},
|
116
|
+
script: "log(doc['#{options[:boost]}'].value + 2.718281828)"
|
117
|
+
}
|
118
|
+
end
|
119
|
+
|
120
|
+
if custom_filters.any?
|
121
|
+
payload = {
|
122
|
+
custom_filters_score: {
|
123
|
+
query: payload,
|
124
|
+
filters: custom_filters,
|
125
|
+
score_mode: "total"
|
126
|
+
}
|
127
|
+
}
|
128
|
+
end
|
129
|
+
|
130
|
+
payload = {
|
131
|
+
query: payload,
|
132
|
+
size: per_page,
|
133
|
+
from: offset
|
134
|
+
}
|
135
|
+
payload[:explain] = options[:explain] if options[:explain]
|
136
|
+
|
137
|
+
# order
|
138
|
+
if options[:order]
|
139
|
+
order = options[:order].is_a?(Enumerable) ? options[:order] : {options[:order] => :asc}
|
140
|
+
payload[:sort] = Hash[ order.map{|k, v| [k.to_s == "id" ? :_id : k, v] } ]
|
141
|
+
end
|
142
|
+
|
143
|
+
term_filters =
|
144
|
+
proc do |field, value|
|
145
|
+
if value.is_a?(Array) # in query
|
146
|
+
if value.any?
|
147
|
+
{or: value.map{|v| term_filters.call(field, v) }}
|
148
|
+
else
|
149
|
+
{terms: {field => value}} # match nothing
|
150
|
+
end
|
151
|
+
elsif value.nil?
|
152
|
+
{missing: {"field" => field, existence: true, null_value: true}}
|
153
|
+
else
|
154
|
+
{term: {field => value}}
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# where
|
159
|
+
where_filters =
|
160
|
+
proc do |where|
|
161
|
+
filters = []
|
162
|
+
(where || {}).each do |field, value|
|
163
|
+
field = :_id if field.to_s == "id"
|
164
|
+
|
165
|
+
if field == :or
|
166
|
+
value.each do |or_clause|
|
167
|
+
filters << {or: or_clause.map{|or_statement| {and: where_filters.call(or_statement)} }}
|
168
|
+
end
|
169
|
+
else
|
170
|
+
# expand ranges
|
171
|
+
if value.is_a?(Range)
|
172
|
+
value = {gte: value.first, (value.exclude_end? ? :lt : :lte) => value.last}
|
173
|
+
end
|
174
|
+
|
175
|
+
if value.is_a?(Hash)
|
176
|
+
if value[:near]
|
177
|
+
filters << {
|
178
|
+
geo_distance: {
|
179
|
+
field => value.delete(:near).map(&:to_f).reverse,
|
180
|
+
distance: value.delete(:within) || "50mi"
|
181
|
+
}
|
182
|
+
}
|
183
|
+
end
|
184
|
+
|
185
|
+
if value[:top_left]
|
186
|
+
filters << {
|
187
|
+
geo_bounding_box: {
|
188
|
+
field => {
|
189
|
+
top_left: value.delete(:top_left).map(&:to_f).reverse,
|
190
|
+
bottom_right: value.delete(:bottom_right).map(&:to_f).reverse
|
191
|
+
}
|
192
|
+
}
|
193
|
+
}
|
194
|
+
end
|
195
|
+
|
196
|
+
value.each do |op, op_value|
|
197
|
+
if op == :not # not equal
|
198
|
+
filters << {not: term_filters.call(field, op_value)}
|
199
|
+
elsif op == :all
|
200
|
+
filters << {terms: {field => op_value, execution: "and"}}
|
201
|
+
else
|
202
|
+
range_query =
|
203
|
+
case op
|
204
|
+
when :gt
|
205
|
+
{from: op_value, include_lower: false}
|
206
|
+
when :gte
|
207
|
+
{from: op_value, include_lower: true}
|
208
|
+
when :lt
|
209
|
+
{to: op_value, include_upper: false}
|
210
|
+
when :lte
|
211
|
+
{to: op_value, include_upper: true}
|
212
|
+
else
|
213
|
+
raise "Unknown where operator"
|
214
|
+
end
|
215
|
+
filters << {range: {field => range_query}}
|
216
|
+
end
|
217
|
+
end
|
218
|
+
else
|
219
|
+
filters << term_filters.call(field, value)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
filters
|
224
|
+
end
|
225
|
+
|
226
|
+
# filters
|
227
|
+
filters = where_filters.call(options[:where])
|
228
|
+
if filters.any?
|
229
|
+
payload[:filter] = {
|
230
|
+
and: filters
|
231
|
+
}
|
232
|
+
end
|
233
|
+
|
234
|
+
# facets
|
235
|
+
facet_limits = {}
|
236
|
+
if options[:facets]
|
237
|
+
facets = options[:facets] || {}
|
238
|
+
if facets.is_a?(Array) # convert to more advanced syntax
|
239
|
+
facets = Hash[ facets.map{|f| [f, {}] } ]
|
240
|
+
end
|
241
|
+
|
242
|
+
payload[:facets] = {}
|
243
|
+
facets.each do |field, facet_options|
|
244
|
+
# ask for extra facets due to
|
245
|
+
# https://github.com/elasticsearch/elasticsearch/issues/1305
|
246
|
+
|
247
|
+
if facet_options[:ranges]
|
248
|
+
payload[:facets][field] = {
|
249
|
+
range: {
|
250
|
+
field.to_sym => facet_options[:ranges]
|
251
|
+
}
|
252
|
+
}
|
253
|
+
else
|
254
|
+
payload[:facets][field] = {
|
255
|
+
terms: {
|
256
|
+
field: field,
|
257
|
+
size: facet_options[:limit] ? facet_options[:limit] + 150 : 100000
|
258
|
+
}
|
259
|
+
}
|
260
|
+
end
|
261
|
+
|
262
|
+
facet_limits[field] = facet_options[:limit] if facet_options[:limit]
|
263
|
+
|
264
|
+
# offset is not possible
|
265
|
+
# http://elasticsearch-users.115913.n3.nabble.com/Is-pagination-possible-in-termsStatsFacet-td3422943.html
|
266
|
+
|
267
|
+
facet_filters = where_filters.call(facet_options[:where])
|
268
|
+
if facet_filters.any?
|
269
|
+
payload[:facets][field][:facet_filter] = {
|
270
|
+
and: {
|
271
|
+
filters: facet_filters
|
272
|
+
}
|
273
|
+
}
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
# highlight
|
279
|
+
if options[:highlight]
|
280
|
+
payload[:highlight] = {
|
281
|
+
fields: Hash[ fields.map{|f| [f, {}] } ]
|
282
|
+
}
|
283
|
+
if options[:highlight].is_a?(Hash) and tag = options[:highlight][:tag]
|
284
|
+
payload[:highlight][:pre_tags] = [tag]
|
285
|
+
payload[:highlight][:post_tags] = [tag.to_s.gsub(/\A</, "</")]
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
payload[:_source] = 'updated_at'
|
290
|
+
|
291
|
+
self.new.merge payload
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|