Module: CouchbaseOrm::Views::ClassMethods

Defined in:
lib/couchbase-orm/views.rb

Constant Summary collapse

ViewDefaults =
{include_docs: true}

Instance Method Summary collapse

Instance Method Details

#ensure_design_document!Object



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/couchbase-orm/views.rb', line 98

def ensure_design_document!
    return false unless @views && !@views.empty?
    existing = {}
    update_required = false

    # Grab the existing view details
    begin
        ddoc = bucket.view_indexes.get_design_document(@design_document, :production)
    rescue Couchbase::Error::DesignDocumentNotFound
    end
    existing = ddoc.views if ddoc
    views_actual = {}
    # Fill in the design documents
    @views.each do |name, document|
        views_actual[name.to_s] = Couchbase::Management::View.new(
            document[:map]&.gsub('{{design_document}}', @design_document),
            document[:reduce]&.gsub('{{design_document}}', @design_document)
        )
    end

    # Check there are no changes we need to apply
    views_actual.each do |name, desired|
        check = existing[name]
        if check
            cmap = (check.map || '').gsub(/\s+/, '')
            creduce = (check.reduce || '').gsub(/\s+/, '')
            dmap = (desired.map || '').gsub(/\s+/, '')
            dreduce = (desired.reduce || '').gsub(/\s+/, '')

            unless cmap == dmap && creduce == dreduce
                update_required = true
                break
            end
        else
            update_required = true
            break
        end
    end

    # Updated the design document
    if update_required
        document = Couchbase::Management::DesignDocument.new
        document.views = views_actual
        document.name = @design_document
        bucket.view_indexes.upsert_design_document(document, :production)

        true
    else
        false
    end
end

#include_docs(view_result) ⇒ Object



150
151
152
153
154
155
156
157
158
# File 'lib/couchbase-orm/views.rb', line 150

def include_docs(view_result)
    if view_result.rows.length > 1
        self.find(view_result.rows.map(&:id))
    elsif view_result.rows.length == 1
        [self.find(view_result.rows.first.id)]
    else
        []
    end
end

#index_view(attr, validate: true, find_method: nil, view_method: nil) ⇒ Object

add a view and lookup method to the model for finding all records using a value in the supplied attr.



84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/couchbase-orm/views.rb', line 84

def index_view(attr, validate: true, find_method: nil, view_method: nil)
    view_method ||= "by_#{attr}"
    find_method ||= "find_#{view_method}"

    validates(attr, presence: true) if validate
    view view_method, emit_key: attr

    instance_eval "
        def self.#{find_method}(#{attr})
            #{view_method}(key: #{attr})
        end
    "
end

#view(name, map: nil, emit_key: nil, reduce: nil, **options) ⇒ Object

Defines a view for the model

Examples:

Define some views for a model

class Post < CouchbaseOrm::Base
  view :all
  view :by_rating, emit_key: :rating
end

Post.by_rating do |response|
  # ...
end

Parameters:

  • names (Symbol, String, Array)

    names of the views

  • options (Hash)

    options passed to the Couchbase::View

Raises:

  • (ArgumentError)


25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/couchbase-orm/views.rb', line 25

def view(name, map: nil, emit_key: nil, reduce: nil, **options)
    raise ArgumentError, "#{self} already respond_to? #{name}" if self.respond_to?(name)
    
    if emit_key.class == Array
        emit_key.each do |key|
            raise "unknown emit_key attribute for view :#{name}, emit_key: :#{key}" if key && !attribute_names.include?(key.to_s)
        end
    else
        raise "unknown emit_key attribute for view :#{name}, emit_key: :#{emit_key}" if emit_key && !attribute_names.include?(emit_key.to_s)
    end

    options = ViewDefaults.merge(options)

    method_opts = {}
    method_opts[:map]    = map    if map
    method_opts[:reduce] = reduce if reduce

    unless method_opts.has_key? :map
        if emit_key.class == Array
            method_opts[:map] = <<-EMAP
function(doc) {
    if (doc.type === "{{design_document}}") {
        emit([#{emit_key.map{|key| "doc."+key.to_s}.join(',')}], null);
    }
}
EMAP
        else
            emit_key = emit_key || :created_at
            method_opts[:map] = <<-EMAP
function(doc) {
    if (doc.type === "{{design_document}}") {
        emit(doc.#{emit_key}, null);
    }
}
EMAP
        end
    end

    @views ||= {}

    name = name.to_sym
    @views[name] = method_opts

    singleton_class.__send__(:define_method, name) do |**opts, &result_modifier|
        opts = options.merge(opts).reverse_merge(scan_consistency: CouchbaseOrm::N1ql.config[:scan_consistency])
        CouchbaseOrm.logger.debug("View [#{@design_document}, #{name.inspect}] options: #{opts.inspect}")
        if result_modifier
            include_docs(bucket.view_query(@design_document, name.to_s, Couchbase::Options::View.new(**opts.except(:include_docs)))).map(&result_modifier)
        elsif opts[:include_docs]
            include_docs(bucket.view_query(@design_document, name.to_s, Couchbase::Options::View.new(**opts.except(:include_docs))))
        else
            bucket.view_query(@design_document, name.to_s, Couchbase::Options::View.new(**opts.except(:include_docs)))
        end
    end
end