solrbee 0.4.0 → 0.5.0
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 +4 -4
- data/lib/rom/solr.rb +19 -14
- data/lib/rom/solr/commands/create_documents.rb +1 -1
- data/lib/rom/solr/commands/delete_documents.rb +1 -1
- data/lib/rom/solr/commands/update_documents.rb +1 -1
- data/lib/rom/solr/dataset.rb +21 -25
- data/lib/rom/solr/document_repo.rb +22 -10
- data/lib/rom/solr/documents_dataset.rb +52 -2
- data/lib/rom/solr/documents_paginator.rb +21 -0
- data/lib/rom/solr/query.rb +135 -0
- data/lib/rom/solr/query_builder.rb +36 -0
- data/lib/rom/solr/query_templates.rb +34 -0
- data/lib/rom/solr/relation.rb +17 -17
- data/lib/rom/solr/relations/documents_relation.rb +98 -43
- data/lib/rom/solr/relations/schema_info_relation.rb +30 -52
- data/lib/rom/solr/request_handler.rb +3 -3
- data/lib/rom/solr/schema_info_dataset.rb +8 -0
- data/lib/rom/solr/schema_info_repo.rb +18 -34
- data/lib/rom/solr/utils.rb +36 -0
- data/lib/solrbee.rb +7 -2
- data/lib/solrbee/version.rb +1 -1
- data/solrbee.gemspec +1 -0
- metadata +21 -3
- data/lib/rom/solr/select_cursor.rb +0 -56
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 32b15ba57c92024c22c90c365c673098bbc5031fb14e76ae62ea75868e0d1ec3
         | 
| 4 | 
            +
              data.tar.gz: 7aa16b385b8ddff45506966c1fbbb9b3e6252cfff2053b4f29377f09b9c4b9a1
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: c20241f1f4ed8e31fee8aa383bcf1a643f3dd94f8a6435d2ba170efe2858b0215472c65434575d62dfe9b40fb3899050c9bf39eb1457443ad29ff744d926f87f
         | 
| 7 | 
            +
              data.tar.gz: '023180eea1874e7d4c17caa61bf39b5b8d5163b5681b4d0356ee8b41c65de4d9a4b32ddc233645d38b780fc6a4ae8345bd3d808d241e2d108021c38e67f9ccc5'
         | 
    
        data/lib/rom/solr.rb
    CHANGED
    
    | @@ -1,9 +1,13 @@ | |
| 1 | 
            -
            require 'rom-http'
         | 
| 2 1 | 
             
            require 'securerandom'
         | 
| 2 | 
            +
            require 'rom-http'
         | 
| 3 3 |  | 
| 4 4 | 
             
            module ROM
         | 
| 5 5 | 
             
              module Solr
         | 
| 6 6 |  | 
| 7 | 
            +
                ESCAPE_CHARS     = Regexp.new('[%s]' % Regexp.escape('+-!(){}[]^"~*?:/'))
         | 
| 8 | 
            +
                DOUBLE_AMPERSAND = Regexp.new('&&')
         | 
| 9 | 
            +
                DOUBLE_PIPE      = Regexp.new('\|\|')
         | 
| 10 | 
            +
             | 
| 7 11 | 
             
                def self.dataset_class(name)
         | 
| 8 12 | 
             
                  prefix = name.to_s.split(/[_\/]/).map(&:capitalize).join('')
         | 
| 9 13 | 
             
                  const_name = "#{prefix}Dataset"
         | 
| @@ -20,32 +24,33 @@ module ROM | |
| 20 24 | 
             
            end
         | 
| 21 25 |  | 
| 22 26 | 
             
            # Utilities
         | 
| 23 | 
            -
             | 
| 27 | 
            +
            require 'rom/solr/array'
         | 
| 28 | 
            +
            require 'rom/solr/utils'
         | 
| 24 29 |  | 
| 25 30 | 
             
            # Handlers
         | 
| 26 | 
            -
             | 
| 27 | 
            -
             | 
| 31 | 
            +
            require 'rom/solr/request_handler'
         | 
| 32 | 
            +
            require 'rom/solr/response_handler'
         | 
| 28 33 |  | 
| 29 34 | 
             
            # Datasets
         | 
| 30 | 
            -
             | 
| 31 | 
            -
             | 
| 32 | 
            -
             | 
| 35 | 
            +
            require 'rom/solr/dataset'
         | 
| 36 | 
            +
            require 'rom/solr/documents_dataset'
         | 
| 37 | 
            +
            require 'rom/solr/schema_info_dataset'
         | 
| 33 38 |  | 
| 34 39 | 
             
            # Gateway
         | 
| 35 | 
            -
             | 
| 40 | 
            +
            require 'rom/solr/gateway'
         | 
| 36 41 |  | 
| 37 42 | 
             
            # Schemas
         | 
| 38 | 
            -
             | 
| 43 | 
            +
            require 'rom/solr/schema'
         | 
| 39 44 |  | 
| 40 45 | 
             
            # Relations
         | 
| 41 | 
            -
             | 
| 46 | 
            +
            require 'rom/solr/relation'
         | 
| 42 47 |  | 
| 43 48 | 
             
            # Repositories
         | 
| 44 | 
            -
             | 
| 45 | 
            -
             | 
| 46 | 
            -
             | 
| 49 | 
            +
            require 'rom/solr/repository'
         | 
| 50 | 
            +
            require 'rom/solr/schema_info_repo'
         | 
| 51 | 
            +
            require 'rom/solr/document_repo'
         | 
| 47 52 |  | 
| 48 53 | 
             
            # Commands
         | 
| 49 | 
            -
             | 
| 54 | 
            +
            require 'rom/solr/commands'
         | 
| 50 55 |  | 
| 51 56 | 
             
            ROM.register_adapter(:solr, ROM::Solr)
         | 
    
        data/lib/rom/solr/dataset.rb
    CHANGED
    
    | @@ -2,44 +2,32 @@ module ROM | |
| 2 2 | 
             
              module Solr
         | 
| 3 3 | 
             
                class Dataset < ROM::HTTP::Dataset
         | 
| 4 4 |  | 
| 5 | 
            -
                  setting : | 
| 6 | 
            -
                  setting :default_content_type, reader: true
         | 
| 7 | 
            -
                  setting :default_base_path,    reader: true
         | 
| 5 | 
            +
                  setting :default_base_path, reader: true
         | 
| 8 6 |  | 
| 9 7 | 
             
                  configure do |config|
         | 
| 10 8 | 
             
                    config.default_response_handler = ResponseHandler
         | 
| 11 9 | 
             
                    config.default_request_handler  = RequestHandler
         | 
| 12 10 | 
             
                  end
         | 
| 13 11 |  | 
| 14 | 
            -
                  option : | 
| 15 | 
            -
                  option : | 
| 16 | 
            -
             | 
| 17 | 
            -
                   | 
| 12 | 
            +
                  option :request_data, type: Types::String, optional: true
         | 
| 13 | 
            +
                  option :content_type, type: Types::String, optional: true
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  # @override
         | 
| 16 | 
            +
                  option :base_path, type: Types::Path, default: proc { self.class.default_base_path || EMPTY_STRING }
         | 
| 18 17 |  | 
| 19 18 | 
             
                  # @override Query parameters are valid with POST, too.
         | 
| 20 19 | 
             
                  def uri
         | 
| 21 20 | 
             
                    uri_s = [options[:uri], path].compact.reject(&:empty?).join('/')
         | 
| 22 21 |  | 
| 23 22 | 
             
                    URI(uri_s).tap do |u|
         | 
| 24 | 
            -
                      u.query = param_encoder.call(params) if  | 
| 23 | 
            +
                      u.query = param_encoder.call(params) if params?
         | 
| 25 24 | 
             
                    end
         | 
| 26 25 | 
             
                  end
         | 
| 27 26 |  | 
| 28 | 
            -
                  # @override
         | 
| 29 | 
            -
                  def each(&block)
         | 
| 30 | 
            -
                    return to_enum unless block_given?
         | 
| 31 | 
            -
             | 
| 32 | 
            -
                    enumerable_data.each(&block)
         | 
| 33 | 
            -
                  end
         | 
| 34 | 
            -
             | 
| 35 27 | 
             
                  def with_request_data(data)
         | 
| 36 28 | 
             
                    with_options(request_data: data)
         | 
| 37 29 | 
             
                  end
         | 
| 38 30 |  | 
| 39 | 
            -
                  def with_response_key(*path)
         | 
| 40 | 
            -
                    with_options(response_key: path)
         | 
| 41 | 
            -
                  end
         | 
| 42 | 
            -
             | 
| 43 31 | 
             
                  # Copies and makes private superclass #response method
         | 
| 44 32 | 
             
                  alias_method :__response__, :response
         | 
| 45 33 | 
             
                  private :__response__
         | 
| @@ -49,20 +37,28 @@ module ROM | |
| 49 37 | 
             
                    cache.fetch_or_store(:response) { __response__ }
         | 
| 50 38 | 
             
                  end
         | 
| 51 39 |  | 
| 52 | 
            -
                  def  | 
| 53 | 
            -
                     | 
| 40 | 
            +
                  def response_header(key)
         | 
| 41 | 
            +
                    response.dig(:responseHeader, key)
         | 
| 42 | 
            +
                  end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                  def request_data?
         | 
| 45 | 
            +
                    !request_data.nil?
         | 
| 54 46 | 
             
                  end
         | 
| 55 47 |  | 
| 56 | 
            -
                  def  | 
| 48 | 
            +
                  def params?
         | 
| 57 49 | 
             
                    params.any?
         | 
| 58 50 | 
             
                  end
         | 
| 59 51 |  | 
| 60 | 
            -
                   | 
| 52 | 
            +
                  def status
         | 
| 53 | 
            +
                    response_header(:status)
         | 
| 54 | 
            +
                  end
         | 
| 61 55 |  | 
| 62 | 
            -
                  def  | 
| 63 | 
            -
                     | 
| 56 | 
            +
                  def qtime
         | 
| 57 | 
            +
                    response_header(:QTime)
         | 
| 64 58 | 
             
                  end
         | 
| 65 59 |  | 
| 60 | 
            +
                  private
         | 
| 61 | 
            +
             | 
| 66 62 | 
             
                  def cache
         | 
| 67 63 | 
             
                    @cache ||= Concurrent::Map.new
         | 
| 68 64 | 
             
                  end
         | 
| @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            require 'rom/solr/query_builder'
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            module ROM
         | 
| 2 4 | 
             
              module Solr
         | 
| 3 5 | 
             
                class DocumentRepo < Repository[:documents]
         | 
| @@ -8,24 +10,34 @@ module ROM | |
| 8 10 | 
             
                    documents.by_unique_key(id).one!
         | 
| 9 11 | 
             
                  end
         | 
| 10 12 |  | 
| 11 | 
            -
                  def search
         | 
| 12 | 
            -
                    documents
         | 
| 13 | 
            -
                  end
         | 
| 14 | 
            -
             | 
| 15 13 | 
             
                  def all
         | 
| 16 14 | 
             
                    documents.all
         | 
| 17 15 | 
             
                  end
         | 
| 18 16 |  | 
| 19 | 
            -
                  def create(docs)
         | 
| 20 | 
            -
                     | 
| 17 | 
            +
                  def create(docs, **opts)
         | 
| 18 | 
            +
                    docs_command(:create_documents, docs, **opts)
         | 
| 19 | 
            +
                  end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                  def delete(docs, **opts)
         | 
| 22 | 
            +
                    docs_command(:delete_documents, docs, **opts)
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                  def delete_by_query(query, **opts)
         | 
| 26 | 
            +
                    docs_command(:delete_documents_by_query, query, **opts)
         | 
| 21 27 | 
             
                  end
         | 
| 22 28 |  | 
| 23 | 
            -
                  def  | 
| 24 | 
            -
                     | 
| 29 | 
            +
                  def update(docs, **opts)
         | 
| 30 | 
            +
                    docs_command(:update_documents, docs, **opts)
         | 
| 25 31 | 
             
                  end
         | 
| 26 32 |  | 
| 27 | 
            -
                   | 
| 28 | 
            -
             | 
| 33 | 
            +
                  private
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                  def docs_command(command, data, commit: false, commit_within: nil)
         | 
| 36 | 
            +
                    documents
         | 
| 37 | 
            +
                      .commit(commit)
         | 
| 38 | 
            +
                      .commit_within(commit_within)
         | 
| 39 | 
            +
                      .command(command)
         | 
| 40 | 
            +
                      .call(data)
         | 
| 29 41 | 
             
                  end
         | 
| 30 42 |  | 
| 31 43 | 
             
                end
         | 
| @@ -3,8 +3,58 @@ module ROM | |
| 3 3 | 
             
                class DocumentsDataset < Dataset
         | 
| 4 4 |  | 
| 5 5 | 
             
                  configure do |config|
         | 
| 6 | 
            -
                    config. | 
| 7 | 
            -
             | 
| 6 | 
            +
                    config.default_base_path = 'select'
         | 
| 7 | 
            +
                  end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                  # @override
         | 
| 10 | 
            +
                  def each(&block)
         | 
| 11 | 
            +
                    return to_enum unless block_given?
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                    docs.each(&block)
         | 
| 14 | 
            +
                  end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  def docs
         | 
| 17 | 
            +
                    search_response(:docs)
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  def cursor_mark
         | 
| 21 | 
            +
                    params[:cursorMark]
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  def next_cursor_mark
         | 
| 25 | 
            +
                    response[:nextCursorMark]
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  def num_found
         | 
| 29 | 
            +
                    search_response(:numFound)
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                  def num_found_exact
         | 
| 33 | 
            +
                    search_response(:numFoundExact)
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                  def num_found_exact?
         | 
| 37 | 
            +
                    num_found_exact === true
         | 
| 38 | 
            +
                  end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                  def search_response(key)
         | 
| 41 | 
            +
                    response.dig(:response, key)
         | 
| 42 | 
            +
                  end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                  def partial_results
         | 
| 45 | 
            +
                    response_header(:partialResults)
         | 
| 46 | 
            +
                  end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                  def partial_results?
         | 
| 49 | 
            +
                    partial_results === true
         | 
| 50 | 
            +
                  end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                  def response_params
         | 
| 53 | 
            +
                    response_header(:params)
         | 
| 54 | 
            +
                  end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                  def response_params?
         | 
| 57 | 
            +
                    !response_params.nil? && !response_params.empty?
         | 
| 8 58 | 
             
                  end
         | 
| 9 59 |  | 
| 10 60 | 
             
                end
         | 
| @@ -0,0 +1,21 @@ | |
| 1 | 
            +
            require 'delegate'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module ROM
         | 
| 4 | 
            +
              module Solr
         | 
| 5 | 
            +
                #
         | 
| 6 | 
            +
                # Wraps a DocumentsDataset to provide pagination with a cursor.
         | 
| 7 | 
            +
                #
         | 
| 8 | 
            +
                class DocumentsPaginator < SimpleDelegator
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                  def each(&block)
         | 
| 11 | 
            +
                    while true
         | 
| 12 | 
            +
                      super
         | 
| 13 | 
            +
                      break if cursor_mark == next_cursor_mark
         | 
| 14 | 
            +
                      next_page = __getobj__.add_params(cursorMark: next_cursor_mark)
         | 
| 15 | 
            +
                      __setobj__(next_page)
         | 
| 16 | 
            +
                    end
         | 
| 17 | 
            +
                  end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
              end
         | 
| 21 | 
            +
            end
         | 
| @@ -0,0 +1,135 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module ROM
         | 
| 4 | 
            +
              module Solr
         | 
| 5 | 
            +
                module Query
         | 
| 6 | 
            +
                  include Utils
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  ANY_VALUE = '[* TO *]'
         | 
| 9 | 
            +
                  OR        = ' OR '
         | 
| 10 | 
            +
                  AND       = ' AND '
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  # Templates
         | 
| 13 | 
            +
                  DISJUNCTION = '{!lucene q.op=OR df=%{field}}%{value}'
         | 
| 14 | 
            +
                  JOIN        = '{!join from=%{from} to=%{to}}%{field}:%{value}'
         | 
| 15 | 
            +
                  NEGATION    = '-%{field}:%{value}'
         | 
| 16 | 
            +
                  REGEXP      = '%{field}:/%{value}/'
         | 
| 17 | 
            +
                  STANDARD    = '%{field}:%{value}'
         | 
| 18 | 
            +
                  TERM        = '{!term f=%{field}}%{value}'
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  # Value transformers
         | 
| 21 | 
            +
                  NOOP          = ->(v) { v }
         | 
| 22 | 
            +
                  ESCAPE        = ->(v) { escape(v) }
         | 
| 23 | 
            +
                  INTEGER       = ->(v) { v.to_i }
         | 
| 24 | 
            +
                  BEFORE_DATE   = ->(v) { '[* TO %s]' % solr_date(v) }
         | 
| 25 | 
            +
                  AFTER_DATE    = ->(v) { '[%s TO NOW]' % solr_date(v) }
         | 
| 26 | 
            +
                  BETWEEN_DATES = ->(a, b) { '[%s TO %s]' % [solr_date(a), solr_date(b)] }
         | 
| 27 | 
            +
                  EXACT_DATE    = ->(v) { escape(solr_date(v)) }
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                  # Build standard query clause(s) -- i.e., field:value --
         | 
| 30 | 
            +
                  # and disjunction clauses (i.e., when value is an array).
         | 
| 31 | 
            +
                  #
         | 
| 32 | 
            +
                  # @param mapping [Hash] field=>value mapping
         | 
| 33 | 
            +
                  # @return [Array<String>] queries
         | 
| 34 | 
            +
                  def where(mapping)
         | 
| 35 | 
            +
                    render(STANDARD, mapping, ->(v) { Array.wrap(v).join(OR) })
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                  # Builds negation clause(s) -- i.e., -field:value
         | 
| 39 | 
            +
                  #
         | 
| 40 | 
            +
                  # @param mapping [Hash] field=>value mapping
         | 
| 41 | 
            +
                  # @return [Array<String>] queries
         | 
| 42 | 
            +
                  def where_not(mapping)
         | 
| 43 | 
            +
                    render(NEGATION, mapping)
         | 
| 44 | 
            +
                  end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                  # Builds query clause(s) to filter where field is present
         | 
| 47 | 
            +
                  # (i.e., has one or more values)
         | 
| 48 | 
            +
                  #
         | 
| 49 | 
            +
                  # @param fields [Array<String>] on or more fields
         | 
| 50 | 
            +
                  # @return [Array<String>] queries
         | 
| 51 | 
            +
                  def exist(*fields)
         | 
| 52 | 
            +
                    mapping = fields.map { |field| {field: field, value: ANY_VALUE} }
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                    render(STANDARD, mapping)
         | 
| 55 | 
            +
                  end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                  # Builds query clause(s) to filter where field is NOT present
         | 
| 58 | 
            +
                  # (i.e., no values)
         | 
| 59 | 
            +
                  #
         | 
| 60 | 
            +
                  # @param fields [Array<Symbol, String>] one or more fields
         | 
| 61 | 
            +
                  # @return [Array<String>] queries
         | 
| 62 | 
            +
                  def not_exist(*fields)
         | 
| 63 | 
            +
                    mapping = fields.map { |field| {field: "-#{field}", value: ANY_VALUE} }
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                    render(STANDARD, mapping)
         | 
| 66 | 
            +
                  end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                  # Builds a Solr join clause
         | 
| 69 | 
            +
                  #
         | 
| 70 | 
            +
                  # @see https://wiki.apache.org/solr/Join
         | 
| 71 | 
            +
                  # @param from [String]
         | 
| 72 | 
            +
                  # @param to [String]
         | 
| 73 | 
            +
                  # @param field [String]
         | 
| 74 | 
            +
                  # @param value [String]
         | 
| 75 | 
            +
                  # @return [Array<String>] queries
         | 
| 76 | 
            +
                  def join(from:, to:, field:, value:)
         | 
| 77 | 
            +
                    [ JOIN % { from: from, to: to, field: field, value: value } ]
         | 
| 78 | 
            +
                  end
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                  # Builds query clause(s) to filter where date field value
         | 
| 81 | 
            +
                  # is earlier than a date/time value.
         | 
| 82 | 
            +
                  #
         | 
| 83 | 
            +
                  # @param mapping [Hash] field=>value mapping
         | 
| 84 | 
            +
                  #   Values (coerced to strings) must be parseable by `DateTime.parse`.
         | 
| 85 | 
            +
                  # @return [Array<String>] queries
         | 
| 86 | 
            +
                  def before(mapping)
         | 
| 87 | 
            +
                    render(STANDARD, mapping, BEFORE_DATE)
         | 
| 88 | 
            +
                  end
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                  # Builds query clause(s) to filter where date field value
         | 
| 91 | 
            +
                  # is later than a date/time value.
         | 
| 92 | 
            +
                  #
         | 
| 93 | 
            +
                  # @param mapping [Hash] field=>value mapping
         | 
| 94 | 
            +
                  #   Values (coerced to strings) must be parseable by `DateTime.parse`.
         | 
| 95 | 
            +
                  # @return [Array<String>] queries
         | 
| 96 | 
            +
                  def after(mapping)
         | 
| 97 | 
            +
                    render(STANDARD, mapping, AFTER_DATE)
         | 
| 98 | 
            +
                  end
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                  def between_dates(mapping)
         | 
| 101 | 
            +
                    render(STANDARD, mapping, BETWEEN_DATES)
         | 
| 102 | 
            +
                  end
         | 
| 103 | 
            +
             | 
| 104 | 
            +
                  def exact_date(mapping)
         | 
| 105 | 
            +
                    render(STANDARD, mapping, EXACT_DATE)
         | 
| 106 | 
            +
                  end
         | 
| 107 | 
            +
             | 
| 108 | 
            +
                  # Builds term query clause(s) to filter where field contains value.
         | 
| 109 | 
            +
                  #
         | 
| 110 | 
            +
                  # @param mapping [Hash] field=>value mapping
         | 
| 111 | 
            +
                  # @return [Array<String>] queries
         | 
| 112 | 
            +
                  def term(mapping)
         | 
| 113 | 
            +
                    render(TERM, mapping)
         | 
| 114 | 
            +
                  end
         | 
| 115 | 
            +
             | 
| 116 | 
            +
                  # Builds regular expression query clause(s).
         | 
| 117 | 
            +
                  #
         | 
| 118 | 
            +
                  # @param mapping [Hash] field=>value mapping
         | 
| 119 | 
            +
                  # @return [Array<String>] queries
         | 
| 120 | 
            +
                  def regexp(mapping)
         | 
| 121 | 
            +
                    render(REGEXP, mapping, ESCAPE_SLASHE)
         | 
| 122 | 
            +
                  end
         | 
| 123 | 
            +
             | 
| 124 | 
            +
                  private
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                  def render(template, mapping, transformer = NOOP)
         | 
| 127 | 
            +
                    mapping.transform_values(&transformer).map do |field, value|
         | 
| 128 | 
            +
                      template % {field: field, value: value}
         | 
| 129 | 
            +
                    end
         | 
| 130 | 
            +
                  end
         | 
| 131 | 
            +
             | 
| 132 | 
            +
                  extend self
         | 
| 133 | 
            +
                end
         | 
| 134 | 
            +
              end
         | 
| 135 | 
            +
            end
         | 
| @@ -0,0 +1,36 @@ | |
| 1 | 
            +
            require 'rom/solr/query'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module ROM
         | 
| 4 | 
            +
              module Solr
         | 
| 5 | 
            +
                class QueryBuilder
         | 
| 6 | 
            +
                  include Utils
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  attr_accessor :queries
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                  def initialize(queries = [])
         | 
| 11 | 
            +
                    @queries = queries
         | 
| 12 | 
            +
                  end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  def to_a
         | 
| 15 | 
            +
                    queries
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  def raw(*queries)
         | 
| 19 | 
            +
                    self.queries += queries
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  def respond_to_missing?(name, include_private = false)
         | 
| 23 | 
            +
                    Query.public_method_defined?(name, false) || super
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  def method_missing(name, *args)
         | 
| 27 | 
            +
                    if Query.respond_to?(name, false)
         | 
| 28 | 
            +
                      self.queries += Query.send(name, *args)
         | 
| 29 | 
            +
                    else
         | 
| 30 | 
            +
                      super
         | 
| 31 | 
            +
                    end
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
              end
         | 
| 36 | 
            +
            end
         | 
| @@ -0,0 +1,34 @@ | |
| 1 | 
            +
            require 'delegate'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module ROM
         | 
| 4 | 
            +
              module Solr
         | 
| 5 | 
            +
                module QueryTemplates
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                  #
         | 
| 8 | 
            +
                  # Simple template object with a `#render` method.
         | 
| 9 | 
            +
                  #
         | 
| 10 | 
            +
                  # N.B. Values must be properly escaped and/or
         | 
| 11 | 
            +
                  # formatted for Solr prior to rendering!
         | 
| 12 | 
            +
                  #
         | 
| 13 | 
            +
                  class Template < SimpleDelegator
         | 
| 14 | 
            +
                    # @param data [Hash]
         | 
| 15 | 
            +
                    def render(data)
         | 
| 16 | 
            +
                      __getobj__ % data
         | 
| 17 | 
            +
                    end
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  STANDARD    = Template.new('%{field}:%{value}')
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  ABSENT      = Template.new('-%{field}:[* TO *]')
         | 
| 23 | 
            +
                  BEFORE_DATE = Template.new('%{field}:[* TO %{value}]')
         | 
| 24 | 
            +
                  BEFORE_DAYS = Template.new('%{field}:[* TO NOW-%{value}DAYS]')
         | 
| 25 | 
            +
                  DISJUNCTION = Template.new('{!lucene q.op=OR df=%{field}}%{value}')
         | 
| 26 | 
            +
                  JOIN        = Template.new('{!join from=%{from} to=%{to}}%{field}:%{value}')
         | 
| 27 | 
            +
                  NEGATION    = Template.new('-%{field}:%{value}')
         | 
| 28 | 
            +
                  PRESENT     = Template.new('%{field}:[* TO *]')
         | 
| 29 | 
            +
                  REGEXP      = Template.new('%{field}:/%{value}/')
         | 
| 30 | 
            +
                  TERM        = Template.new('{!term f=%{field}}%{value}')
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
              end
         | 
| 34 | 
            +
            end
         | 
    
        data/lib/rom/solr/relation.rb
    CHANGED
    
    | @@ -1,37 +1,37 @@ | |
| 1 | 
            +
            require 'forwardable'
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            module ROM
         | 
| 2 4 | 
             
              module Solr
         | 
| 3 5 | 
             
                class Relation < ROM::HTTP::Relation
         | 
| 6 | 
            +
                  extend Forwardable
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  RESPONSE_WRITERS = %w[ csv geojson javabin json php phps python ruby smile velocity xlsx xml xslt ]
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                  def_delegators :dataset, :response, :params
         | 
| 4 11 |  | 
| 5 12 | 
             
                  adapter     :solr
         | 
| 6 13 | 
             
                  auto_struct false
         | 
| 7 14 | 
             
                  auto_map    false
         | 
| 8 15 |  | 
| 9 | 
            -
                   | 
| 10 | 
            -
             | 
| 16 | 
            +
                  # Need this?
         | 
| 11 17 | 
             
                  option :output_schema, default: ->{ NOOP_OUTPUT_SCHEMA }
         | 
| 12 18 |  | 
| 13 | 
            -
                  def wt( | 
| 14 | 
            -
                    add_params(wt: Types::Coercible::String[ | 
| 19 | 
            +
                  def wt(writer)
         | 
| 20 | 
            +
                    add_params(wt: Types::Coercible::String.enum(*RESPONSE_WRITERS)[writer])
         | 
| 15 21 | 
             
                  end
         | 
| 16 | 
            -
                  alias_method :format, :wt
         | 
| 17 | 
            -
             | 
| 18 | 
            -
                  def log_params_list(log_params)
         | 
| 19 | 
            -
                    lplist = log_params.nil? ? nil : Array.wrap(log_params).join(',')
         | 
| 20 22 |  | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 23 | 
            -
                  alias_method :log_params, :log_params_list
         | 
| 23 | 
            +
                  def log_params_list(*log_params)
         | 
| 24 | 
            +
                    new_log_params = Types::Array.of(Types::String)[log_params]
         | 
| 24 25 |  | 
| 25 | 
            -
             | 
| 26 | 
            -
                    to_enum.count
         | 
| 26 | 
            +
                    add_params(logParamsList: new_log_params.join(','))
         | 
| 27 27 | 
             
                  end
         | 
| 28 28 |  | 
| 29 | 
            -
                  def  | 
| 30 | 
            -
                     | 
| 29 | 
            +
                  def omit_header(omit = true)
         | 
| 30 | 
            +
                    add_params(omitHeader: Types::Bool[omit])
         | 
| 31 31 | 
             
                  end
         | 
| 32 32 |  | 
| 33 | 
            -
                  def  | 
| 34 | 
            -
                     | 
| 33 | 
            +
                  def count
         | 
| 34 | 
            +
                    to_enum.count
         | 
| 35 35 | 
             
                  end
         | 
| 36 36 |  | 
| 37 37 | 
             
                end
         | 
| @@ -1,16 +1,21 @@ | |
| 1 | 
            -
            require 'rom/solr/ | 
| 1 | 
            +
            require 'rom/solr/documents_paginator'
         | 
| 2 | 
            +
            require 'rom/solr/query_builder'
         | 
| 2 3 |  | 
| 3 4 | 
             
            module ROM
         | 
| 4 5 | 
             
              module Solr
         | 
| 5 6 | 
             
                class DocumentsRelation < Relation
         | 
| 6 7 |  | 
| 8 | 
            +
                  def_delegators :dataset, :num_found, :num_found_exact?
         | 
| 9 | 
            +
             | 
| 7 10 | 
             
                  schema(:documents) { }
         | 
| 8 11 |  | 
| 9 12 | 
             
                  # @override
         | 
| 10 13 | 
             
                  def each(&block)
         | 
| 14 | 
            +
                    return super unless cursor?
         | 
| 15 | 
            +
             | 
| 11 16 | 
             
                    return to_enum unless block_given?
         | 
| 12 17 |  | 
| 13 | 
            -
                     | 
| 18 | 
            +
                    DocumentsPaginator.new(dataset).each(&block)
         | 
| 14 19 | 
             
                  end
         | 
| 15 20 |  | 
| 16 21 | 
             
                  # @override FIXME: Get from Solr schema unique key
         | 
| @@ -28,36 +33,74 @@ module ROM | |
| 28 33 |  | 
| 29 34 | 
             
                  # @override
         | 
| 30 35 | 
             
                  def count
         | 
| 31 | 
            -
                    num_found_exact ? num_found : super
         | 
| 36 | 
            +
                    num_found_exact? ? num_found : super
         | 
| 37 | 
            +
                  end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                  # Set a cursor on the relation for pagination
         | 
| 40 | 
            +
                  def cursor
         | 
| 41 | 
            +
                    relation = add_params(cursorMark: '*')
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                    # Sort must include a sort on unique key (id).
         | 
| 44 | 
            +
                    if /\bid\b/ =~ params[:sort]
         | 
| 45 | 
            +
                      relation
         | 
| 46 | 
            +
                    else
         | 
| 47 | 
            +
                      relation.sort(params[:sort], 'id ASC')
         | 
| 48 | 
            +
                    end
         | 
| 32 49 | 
             
                  end
         | 
| 33 50 |  | 
| 34 | 
            -
                  def  | 
| 35 | 
            -
                     | 
| 51 | 
            +
                  def cursor?
         | 
| 52 | 
            +
                    params.key?(:cursorMark)
         | 
| 36 53 | 
             
                  end
         | 
| 37 54 |  | 
| 38 | 
            -
                  def  | 
| 39 | 
            -
                     | 
| 55 | 
            +
                  def query(&block)
         | 
| 56 | 
            +
                    queries = QueryBuilder.new.instance_eval(&block).to_a
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                    q(*queries)
         | 
| 59 | 
            +
                  end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                  def filter(&block)
         | 
| 62 | 
            +
                    queries = QueryBuilder.new.instance_eval(&block).to_a
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                    fq(*queries)
         | 
| 40 65 | 
             
                  end
         | 
| 41 66 |  | 
| 67 | 
            +
                  #
         | 
| 68 | 
            +
                  # Commands
         | 
| 69 | 
            +
                  #
         | 
| 70 | 
            +
             | 
| 42 71 | 
             
                  def insert(docs)
         | 
| 72 | 
            +
                    # FIXME: use input schema
         | 
| 43 73 | 
             
                    data = Array.wrap(docs).map do |doc|
         | 
| 44 74 | 
             
                      doc.transform_keys!(&:to_sym)
         | 
| 45 75 | 
             
                      doc[:id] ||= UUID[]
         | 
| 76 | 
            +
                      doc
         | 
| 46 77 | 
             
                    end
         | 
| 47 78 |  | 
| 48 | 
            -
                     | 
| 79 | 
            +
                    with_options(
         | 
| 80 | 
            +
                      base_path: 'update/json/docs',
         | 
| 81 | 
            +
                      content_type: 'application/json',
         | 
| 82 | 
            +
                      request_data: JSON.dump(data)
         | 
| 83 | 
            +
                    )
         | 
| 49 84 | 
             
                  end
         | 
| 50 85 |  | 
| 51 86 | 
             
                  def update(docs)
         | 
| 87 | 
            +
                    # FIXME: use input schema
         | 
| 88 | 
            +
                    data = Array.wrap(docs)
         | 
| 89 | 
            +
                             .map    { |doc| doc.transform_keys(&:to_sym) }
         | 
| 90 | 
            +
                             .select { |doc| doc.key?(:id) }
         | 
| 91 | 
            +
             | 
| 52 92 | 
             
                    with_options(
         | 
| 53 93 | 
             
                      base_path: 'update/json/docs',
         | 
| 54 94 | 
             
                      content_type: 'application/json',
         | 
| 55 | 
            -
                      request_data: JSON.dump( | 
| 95 | 
            +
                      request_data: JSON.dump(data)
         | 
| 56 96 | 
             
                    )
         | 
| 57 97 | 
             
                  end
         | 
| 58 98 |  | 
| 59 99 | 
             
                  def delete(docs)
         | 
| 60 | 
            -
                     | 
| 100 | 
            +
                    # FIXME: use input schema
         | 
| 101 | 
            +
                    ids = Array.wrap(docs)
         | 
| 102 | 
            +
                            .map { |doc| doc.transform_keys(&:to_sym) }
         | 
| 103 | 
            +
                            .map { |doc| doc.fetch(:id) }
         | 
| 61 104 |  | 
| 62 105 | 
             
                    with_options(
         | 
| 63 106 | 
             
                      base_path: 'update',
         | 
| @@ -66,93 +109,105 @@ module ROM | |
| 66 109 | 
             
                    )
         | 
| 67 110 | 
             
                  end
         | 
| 68 111 |  | 
| 112 | 
            +
                  def delete_by_query(query)
         | 
| 113 | 
            +
                    with_options(
         | 
| 114 | 
            +
                      base_path: 'update',
         | 
| 115 | 
            +
                      content_type: 'application/json',
         | 
| 116 | 
            +
                      request_data: JSON.dump(delete: {query: query})
         | 
| 117 | 
            +
                    )
         | 
| 118 | 
            +
                  end
         | 
| 119 | 
            +
             | 
| 69 120 | 
             
                  #
         | 
| 70 | 
            -
                  #  | 
| 121 | 
            +
                  # Common Query Parameters
         | 
| 71 122 | 
             
                  #
         | 
| 72 | 
            -
             | 
| 73 | 
            -
             | 
| 123 | 
            +
             | 
| 124 | 
            +
                  def q(*queries)
         | 
| 125 | 
            +
                    old_queries = Array.wrap(params[:q])
         | 
| 126 | 
            +
                    new_queries = Array.wrap(queries).flatten
         | 
| 127 | 
            +
             | 
| 128 | 
            +
                    add_params q: (old_queries + new_queries).join(' ')
         | 
| 74 129 | 
             
                  end
         | 
| 75 | 
            -
                  alias_method :query, :q
         | 
| 76 130 |  | 
| 77 | 
            -
                  def fq(* | 
| 78 | 
            -
                     | 
| 131 | 
            +
                  def fq(*queries)
         | 
| 132 | 
            +
                    old_queries = Array.wrap(params[:fq])
         | 
| 133 | 
            +
                    new_queries = Array.wrap(queries).flatten
         | 
| 134 | 
            +
             | 
| 135 | 
            +
                    add_params fq: old_queries + new_queries
         | 
| 79 136 | 
             
                  end
         | 
| 80 | 
            -
                  alias_method :filter, :fq
         | 
| 81 137 |  | 
| 82 138 | 
             
                  def fl(*fields)
         | 
| 83 | 
            -
                     | 
| 139 | 
            +
                    new_fields = Types::Array.of(Types::Coercible::String)[fields]
         | 
| 140 | 
            +
             | 
| 141 | 
            +
                    add_params fl: new_fields.join(',')
         | 
| 84 142 | 
             
                  end
         | 
| 143 | 
            +
             | 
| 85 144 | 
             
                  alias_method :fields, :fl
         | 
| 86 145 |  | 
| 87 146 | 
             
                  def cache(enabled = true)
         | 
| 88 | 
            -
                    add_params | 
| 147 | 
            +
                    add_params cache: Types::Bool[enabled]
         | 
| 89 148 | 
             
                  end
         | 
| 90 149 |  | 
| 91 150 | 
             
                  def segment_terminate_early(enabled = true)
         | 
| 92 | 
            -
                    add_params | 
| 151 | 
            +
                    add_params segmentTerminateEarly: Types::Bool[enabled]
         | 
| 93 152 | 
             
                  end
         | 
| 94 153 |  | 
| 95 154 | 
             
                  def time_allowed(millis)
         | 
| 96 | 
            -
                    add_params | 
| 155 | 
            +
                    add_params timeAllowed: Types::Coercible::Integer[millis]
         | 
| 97 156 | 
             
                  end
         | 
| 98 157 |  | 
| 99 158 | 
             
                  def explain_other(query)
         | 
| 100 | 
            -
                    add_params | 
| 101 | 
            -
                  end
         | 
| 102 | 
            -
             | 
| 103 | 
            -
                  def omit_header(omit = true)
         | 
| 104 | 
            -
                    add_params(omitHeader: Types::Bool[omit])
         | 
| 159 | 
            +
                    add_params explainOther: Types::String[query]
         | 
| 105 160 | 
             
                  end
         | 
| 106 161 |  | 
| 107 162 | 
             
                  def start(offset)
         | 
| 108 | 
            -
                    add_params | 
| 163 | 
            +
                    add_params start: Types::Coercible::Integer[offset]
         | 
| 109 164 | 
             
                  end
         | 
| 110 165 |  | 
| 111 166 | 
             
                  def sort(*criteria)
         | 
| 112 | 
            -
                     | 
| 167 | 
            +
                    new_sort = Types::Array.of(Types::String)[criteria]
         | 
| 168 | 
            +
             | 
| 169 | 
            +
                    add_params sort: new_sort.join(',')
         | 
| 113 170 | 
             
                  end
         | 
| 114 171 |  | 
| 115 172 | 
             
                  def rows(num)
         | 
| 116 | 
            -
                    add_params | 
| 173 | 
            +
                    add_params rows: Types::Coercible::Integer[num]
         | 
| 117 174 | 
             
                  end
         | 
| 175 | 
            +
             | 
| 118 176 | 
             
                  alias_method :limit, :rows
         | 
| 119 177 |  | 
| 120 178 | 
             
                  def def_type(value)
         | 
| 121 | 
            -
                    add_params | 
| 179 | 
            +
                    add_params defType: Types::Coercible::String[value]
         | 
| 122 180 | 
             
                  end
         | 
| 123 181 |  | 
| 124 182 | 
             
                  def debug(setting)
         | 
| 125 | 
            -
                     | 
| 126 | 
            -
                             .enum('query', 'timing', 'results', 'all', 'true')
         | 
| 127 | 
            -
             | 
| 128 | 
            -
                    add_params(debug: type[setting])
         | 
| 183 | 
            +
                    add_params debug: Types::Coercible::String.enum('query', 'timing', 'results', 'all', 'true')[setting]
         | 
| 129 184 | 
             
                  end
         | 
| 130 185 |  | 
| 131 186 | 
             
                  def echo_params(setting)
         | 
| 132 | 
            -
                     | 
| 133 | 
            -
                    add_params(echoParams: type[setting])
         | 
| 187 | 
            +
                    add_params echoParams: Types::Coercible::String.enum('explicit', 'all', 'none')[setting]
         | 
| 134 188 | 
             
                  end
         | 
| 135 189 |  | 
| 136 190 | 
             
                  def min_exact_count(num)
         | 
| 137 | 
            -
                    add_params | 
| 191 | 
            +
                    add_params minExactCount: Types::Coercible::Integer[num]
         | 
| 138 192 | 
             
                  end
         | 
| 139 193 |  | 
| 194 | 
            +
                  #
         | 
| 195 | 
            +
                  # Commit parameters
         | 
| 196 | 
            +
                  #
         | 
| 140 197 | 
             
                  def commit(value = true)
         | 
| 141 | 
            -
                    add_params | 
| 198 | 
            +
                    add_params commit: Types::Bool[value]
         | 
| 142 199 | 
             
                  end
         | 
| 143 200 |  | 
| 144 201 | 
             
                  def commit_within(millis)
         | 
| 145 | 
            -
                     | 
| 146 | 
            -
             | 
| 147 | 
            -
                    add_params(commitWithin: Types::Coercible::Integer[millis])
         | 
| 202 | 
            +
                    add_params commitWithin: Types::Coercible::Integer.optional[millis]
         | 
| 148 203 | 
             
                  end
         | 
| 149 204 |  | 
| 150 205 | 
             
                  def overwrite(value = true)
         | 
| 151 | 
            -
                    add_params | 
| 206 | 
            +
                    add_params overwrite: Types::Bool[value]
         | 
| 152 207 | 
             
                  end
         | 
| 153 208 |  | 
| 154 209 | 
             
                  def expunge_deletes(value = true)
         | 
| 155 | 
            -
                    add_params | 
| 210 | 
            +
                    add_params expungeDeletes: Types::Bool[value]
         | 
| 156 211 | 
             
                  end
         | 
| 157 212 |  | 
| 158 213 | 
             
                end
         | 
| @@ -15,84 +15,62 @@ module ROM | |
| 15 15 | 
             
                  end
         | 
| 16 16 |  | 
| 17 17 | 
             
                  def info
         | 
| 18 | 
            -
                     | 
| 18 | 
            +
                    self
         | 
| 19 19 | 
             
                  end
         | 
| 20 20 |  | 
| 21 | 
            -
                  def copy_fields
         | 
| 22 | 
            -
                     | 
| 23 | 
            -
             | 
| 24 | 
            -
             | 
| 25 | 
            -
                    )
         | 
| 21 | 
            +
                  def copy_fields(source_fields: nil, dest_fields: nil)
         | 
| 22 | 
            +
                    source_fl = Array.wrap(source_fields).join(',') unless source_fields.nil?
         | 
| 23 | 
            +
                    dest_fl   = Array.wrap(dest_fields).join(',') unless dest_fields.nil?
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                    with_path(:copyfields)
         | 
| 26 | 
            +
                      .add_params('source.fl'=>source_fl, 'dest.fl'=>dest_fl)
         | 
| 26 27 | 
             
                  end
         | 
| 27 28 |  | 
| 28 | 
            -
                  def dynamic_fields
         | 
| 29 | 
            -
                     | 
| 30 | 
            -
                       | 
| 31 | 
            -
                      response_key: :dynamicFields
         | 
| 32 | 
            -
                    )
         | 
| 29 | 
            +
                  def dynamic_fields(defaults: true)
         | 
| 30 | 
            +
                    with_path(:dynamicfields)
         | 
| 31 | 
            +
                      .show_defaults(defaults)
         | 
| 33 32 | 
             
                  end
         | 
| 34 33 |  | 
| 35 | 
            -
                  def dynamic_field(name)
         | 
| 36 | 
            -
                     | 
| 37 | 
            -
                       | 
| 38 | 
            -
                      response_key: :dynamicField
         | 
| 39 | 
            -
                    )
         | 
| 34 | 
            +
                  def dynamic_field(name, defaults: true)
         | 
| 35 | 
            +
                    with_path("dynamicfields/#{name}")
         | 
| 36 | 
            +
                      .show_defaults(defaults)
         | 
| 40 37 | 
             
                  end
         | 
| 41 38 |  | 
| 42 39 | 
             
                  def similarity
         | 
| 43 | 
            -
                     | 
| 44 | 
            -
                      path: :similarity,
         | 
| 45 | 
            -
                      response_key: :similarity
         | 
| 46 | 
            -
                    )
         | 
| 40 | 
            +
                    with_path :similarity
         | 
| 47 41 | 
             
                  end
         | 
| 48 42 |  | 
| 49 43 | 
             
                  def unique_key
         | 
| 50 | 
            -
                     | 
| 51 | 
            -
                      path: :uniquekey,
         | 
| 52 | 
            -
                      response_key: :uniqueKey
         | 
| 53 | 
            -
                    )
         | 
| 44 | 
            +
                    with_path :uniquekey
         | 
| 54 45 | 
             
                  end
         | 
| 55 46 |  | 
| 56 47 | 
             
                  def version
         | 
| 57 | 
            -
                     | 
| 58 | 
            -
                      path: :version,
         | 
| 59 | 
            -
                      response_key: :version
         | 
| 60 | 
            -
                    )
         | 
| 48 | 
            +
                    with_path :version
         | 
| 61 49 | 
             
                  end
         | 
| 62 50 |  | 
| 63 51 | 
             
                  def schema_name
         | 
| 64 | 
            -
                     | 
| 65 | 
            -
                      path: :name,
         | 
| 66 | 
            -
                      response_key: :name
         | 
| 67 | 
            -
                    )
         | 
| 52 | 
            +
                    with_path :name
         | 
| 68 53 | 
             
                  end
         | 
| 69 54 |  | 
| 70 | 
            -
                  def fields
         | 
| 71 | 
            -
                     | 
| 72 | 
            -
                       | 
| 73 | 
            -
                       | 
| 74 | 
            -
                    )
         | 
| 55 | 
            +
                  def fields(dynamic: true, defaults: true)
         | 
| 56 | 
            +
                    with_path(:fields)
         | 
| 57 | 
            +
                      .include_dynamic(dynamic)
         | 
| 58 | 
            +
                      .show_defaults(defaults)
         | 
| 75 59 | 
             
                  end
         | 
| 76 60 |  | 
| 77 | 
            -
                  def field(name)
         | 
| 78 | 
            -
                     | 
| 79 | 
            -
                       | 
| 80 | 
            -
                      response_key: :field
         | 
| 81 | 
            -
                    )
         | 
| 61 | 
            +
                  def field(name, defaults: true)
         | 
| 62 | 
            +
                    with_path("fields/#{name}")
         | 
| 63 | 
            +
                      .show_defaults(defaults)
         | 
| 82 64 | 
             
                  end
         | 
| 83 65 |  | 
| 84 | 
            -
                  def field_types
         | 
| 85 | 
            -
                     | 
| 86 | 
            -
                       | 
| 87 | 
            -
                      response_key: :fieldTypes
         | 
| 88 | 
            -
                    )
         | 
| 66 | 
            +
                  def field_types(defaults: true)
         | 
| 67 | 
            +
                    with_path(:fieldtypes)
         | 
| 68 | 
            +
                      .show_defaults(defaults)
         | 
| 89 69 | 
             
                  end
         | 
| 90 70 |  | 
| 91 | 
            -
                  def field_type(name)
         | 
| 92 | 
            -
                     | 
| 93 | 
            -
                       | 
| 94 | 
            -
                      response_key: :fieldType
         | 
| 95 | 
            -
                    )
         | 
| 71 | 
            +
                  def field_type(name, defaults: true)
         | 
| 72 | 
            +
                    with_path("fieldtypes/#{name}")
         | 
| 73 | 
            +
                      .show_defaults(defaults)
         | 
| 96 74 | 
             
                  end
         | 
| 97 75 |  | 
| 98 76 | 
             
                end
         | 
| @@ -20,7 +20,7 @@ module ROM | |
| 20 20 |  | 
| 21 21 | 
             
                  def request
         | 
| 22 22 | 
             
                    request_class.new(uri.request_uri, headers).tap do |req|
         | 
| 23 | 
            -
                      if dataset. | 
| 23 | 
            +
                      if dataset.request_data?
         | 
| 24 24 | 
             
                        req.body = dataset.request_data
         | 
| 25 25 | 
             
                        req.content_type = dataset.content_type
         | 
| 26 26 | 
             
                      end
         | 
| @@ -33,14 +33,14 @@ module ROM | |
| 33 33 |  | 
| 34 34 | 
             
                  def uri
         | 
| 35 35 | 
             
                    @uri ||= URI(dataset.uri).tap do |u|
         | 
| 36 | 
            -
                      if dataset. | 
| 36 | 
            +
                      if dataset.params?
         | 
| 37 37 | 
             
                        u.query ||= URI.encode_www_form(dataset.params)
         | 
| 38 38 | 
             
                      end
         | 
| 39 39 | 
             
                    end
         | 
| 40 40 | 
             
                  end
         | 
| 41 41 |  | 
| 42 42 | 
             
                  def request_class
         | 
| 43 | 
            -
                    if dataset. | 
| 43 | 
            +
                    if dataset.request_data?
         | 
| 44 44 | 
             
                      Net::HTTP::Post
         | 
| 45 45 | 
             
                    else
         | 
| 46 46 | 
             
                      Net::HTTP::Get
         | 
| @@ -4,6 +4,14 @@ module ROM | |
| 4 4 |  | 
| 5 5 | 
             
                  configure do |config|
         | 
| 6 6 | 
             
                    config.default_base_path = 'schema'
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                    config.default_response_handler = Proc.new do |*args|
         | 
| 9 | 
            +
                      ResponseHandler.call(*args).values.flatten
         | 
| 10 | 
            +
                    end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                    config.default_request_handler = Proc.new do |dataset|
         | 
| 13 | 
            +
                      RequestHandler.call(dataset.add_params(omitHeader: true))
         | 
| 14 | 
            +
                    end
         | 
| 7 15 | 
             
                  end
         | 
| 8 16 |  | 
| 9 17 | 
             
                end
         | 
| @@ -1,43 +1,27 @@ | |
| 1 | 
            +
            require 'forwardable'
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            module ROM
         | 
| 2 4 | 
             
              module Solr
         | 
| 3 5 | 
             
                class SchemaInfoRepo < Repository[:schema_info]
         | 
| 6 | 
            +
                  extend Forwardable
         | 
| 4 7 |  | 
| 5 8 | 
             
                  auto_struct false
         | 
| 6 9 |  | 
| 7 | 
            -
                  %i[ schema_name | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 13 | 
            -
             | 
| 14 | 
            -
             | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 20 | 
            -
             | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 23 | 
            -
                  def field_types(defaults: true)
         | 
| 24 | 
            -
                    schema_info.field_types.show_defaults(defaults)
         | 
| 25 | 
            -
                  end
         | 
| 26 | 
            -
             | 
| 27 | 
            -
                  def field_type(name, defaults: true)
         | 
| 28 | 
            -
                    schema_info.field_type(name).show_defaults(defaults).one
         | 
| 29 | 
            -
                  end
         | 
| 30 | 
            -
             | 
| 31 | 
            -
                  def dynamic_fields
         | 
| 32 | 
            -
                    schema_info.dynamic_fields
         | 
| 33 | 
            -
                  end
         | 
| 34 | 
            -
             | 
| 35 | 
            -
                  def dynamic_field(name, defaults: true)
         | 
| 36 | 
            -
                    schema_info.dynamic_field(name).show_defaults(defaults).one
         | 
| 37 | 
            -
                  end
         | 
| 38 | 
            -
             | 
| 39 | 
            -
                  def copy_fields
         | 
| 40 | 
            -
                    schema_info.copy_fields
         | 
| 10 | 
            +
                  VALUES = %i[ schema_name
         | 
| 11 | 
            +
                               similarity
         | 
| 12 | 
            +
                               unique_key
         | 
| 13 | 
            +
                               version
         | 
| 14 | 
            +
                               info
         | 
| 15 | 
            +
                               field
         | 
| 16 | 
            +
                               field_type
         | 
| 17 | 
            +
                               dynamic_field
         | 
| 18 | 
            +
                               copy_field
         | 
| 19 | 
            +
                               ]
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                  def_delegators :schema_info, :fields, :field_types, :dynamic_fields, :copy_fields
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                  VALUES.each do |name|
         | 
| 24 | 
            +
                    define_method name, proc { |*args| schema_info.send(name, *args).one! }
         | 
| 41 25 | 
             
                  end
         | 
| 42 26 |  | 
| 43 27 | 
             
                end
         | 
| @@ -0,0 +1,36 @@ | |
| 1 | 
            +
            require 'date'
         | 
| 2 | 
            +
            require 'time'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module ROM
         | 
| 5 | 
            +
              module Solr
         | 
| 6 | 
            +
                module Utils
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  def phrase(value)
         | 
| 9 | 
            +
                    if value.match?(/ /)
         | 
| 10 | 
            +
                      '"%s"' % value.gsub(/"/, '\"')
         | 
| 11 | 
            +
                    else
         | 
| 12 | 
            +
                      value
         | 
| 13 | 
            +
                    end
         | 
| 14 | 
            +
                  end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  # Escape a Solr query value
         | 
| 17 | 
            +
                  #
         | 
| 18 | 
            +
                  # @param value [String] raw value
         | 
| 19 | 
            +
                  # @return [String] escaped value
         | 
| 20 | 
            +
                  def escape(value)
         | 
| 21 | 
            +
                    value
         | 
| 22 | 
            +
                      .gsub(ESCAPE_CHARS, '\\1')
         | 
| 23 | 
            +
                      .gsub(DOUBLE_AMPERSAND, '\&\&')
         | 
| 24 | 
            +
                      .gsub(DOUBLE_PIPE, '\|\|')
         | 
| 25 | 
            +
                  end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  # Formats a value as a Solr date.
         | 
| 28 | 
            +
                  def solr_date(value)
         | 
| 29 | 
            +
                    DateTime.parse(value.to_s).to_time.utc.iso8601
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                  extend self
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
              end
         | 
| 36 | 
            +
            end
         | 
    
        data/lib/solrbee.rb
    CHANGED
    
    | @@ -1,6 +1,7 @@ | |
| 1 | 
            -
            require  | 
| 1 | 
            +
            require 'solrbee/version'
         | 
| 2 2 |  | 
| 3 | 
            -
            require  | 
| 3 | 
            +
            require 'dry-types'
         | 
| 4 | 
            +
            require 'rom/solr'
         | 
| 4 5 |  | 
| 5 6 | 
             
            module Solrbee
         | 
| 6 7 |  | 
| @@ -25,4 +26,8 @@ module Solrbee | |
| 25 26 | 
             
                end
         | 
| 26 27 | 
             
              end
         | 
| 27 28 |  | 
| 29 | 
            +
              module Types
         | 
| 30 | 
            +
                include Dry.Types()
         | 
| 31 | 
            +
              end
         | 
| 32 | 
            +
             | 
| 28 33 | 
             
            end
         | 
    
        data/lib/solrbee/version.rb
    CHANGED
    
    
    
        data/solrbee.gemspec
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: solrbee
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0. | 
| 4 | 
            +
              version: 0.5.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - David Chandek-Stark
         | 
| 8 8 | 
             
            autorequire:
         | 
| 9 9 | 
             
            bindir: exe
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date:  | 
| 11 | 
            +
            date: 2021-01-04 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: rom
         | 
| @@ -38,6 +38,20 @@ dependencies: | |
| 38 38 | 
             
                - - ">="
         | 
| 39 39 | 
             
                  - !ruby/object:Gem::Version
         | 
| 40 40 | 
             
                    version: '0'
         | 
| 41 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 42 | 
            +
              name: dry-types
         | 
| 43 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 44 | 
            +
                requirements:
         | 
| 45 | 
            +
                - - ">="
         | 
| 46 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 47 | 
            +
                    version: '0'
         | 
| 48 | 
            +
              type: :runtime
         | 
| 49 | 
            +
              prerelease: false
         | 
| 50 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 51 | 
            +
                requirements:
         | 
| 52 | 
            +
                - - ">="
         | 
| 53 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 54 | 
            +
                    version: '0'
         | 
| 41 55 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 42 56 | 
             
              name: bundler
         | 
| 43 57 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -121,7 +135,11 @@ files: | |
| 121 135 | 
             
            - lib/rom/solr/dataset.rb
         | 
| 122 136 | 
             
            - lib/rom/solr/document_repo.rb
         | 
| 123 137 | 
             
            - lib/rom/solr/documents_dataset.rb
         | 
| 138 | 
            +
            - lib/rom/solr/documents_paginator.rb
         | 
| 124 139 | 
             
            - lib/rom/solr/gateway.rb
         | 
| 140 | 
            +
            - lib/rom/solr/query.rb
         | 
| 141 | 
            +
            - lib/rom/solr/query_builder.rb
         | 
| 142 | 
            +
            - lib/rom/solr/query_templates.rb
         | 
| 125 143 | 
             
            - lib/rom/solr/relation.rb
         | 
| 126 144 | 
             
            - lib/rom/solr/relations/documents_relation.rb
         | 
| 127 145 | 
             
            - lib/rom/solr/relations/schema_info_relation.rb
         | 
| @@ -131,7 +149,7 @@ files: | |
| 131 149 | 
             
            - lib/rom/solr/schema.rb
         | 
| 132 150 | 
             
            - lib/rom/solr/schema_info_dataset.rb
         | 
| 133 151 | 
             
            - lib/rom/solr/schema_info_repo.rb
         | 
| 134 | 
            -
            - lib/rom/solr/ | 
| 152 | 
            +
            - lib/rom/solr/utils.rb
         | 
| 135 153 | 
             
            - lib/solrbee.rb
         | 
| 136 154 | 
             
            - lib/solrbee/version.rb
         | 
| 137 155 | 
             
            - solrbee.gemspec
         | 
| @@ -1,56 +0,0 @@ | |
| 1 | 
            -
            require 'delegate'
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            module ROM
         | 
| 4 | 
            -
              module Solr
         | 
| 5 | 
            -
                #
         | 
| 6 | 
            -
                # Wraps a DocumentsDataset to provide pagination with a cursor.
         | 
| 7 | 
            -
                #
         | 
| 8 | 
            -
                class SelectCursor < SimpleDelegator
         | 
| 9 | 
            -
             | 
| 10 | 
            -
                  def initialize(dataset)
         | 
| 11 | 
            -
                    params = { cursorMark: '*' }
         | 
| 12 | 
            -
             | 
| 13 | 
            -
                    # Sort must include a sort on unique key (id).
         | 
| 14 | 
            -
                    sort = dataset.params[:sort]
         | 
| 15 | 
            -
                    unless /\bid\b/ =~ sort
         | 
| 16 | 
            -
                      params[:sort] = Array.wrap(sort).append('id ASC').join(',')
         | 
| 17 | 
            -
                    end
         | 
| 18 | 
            -
             | 
| 19 | 
            -
                    super dataset.add_params(params)
         | 
| 20 | 
            -
                  end
         | 
| 21 | 
            -
             | 
| 22 | 
            -
                  def each(&block)
         | 
| 23 | 
            -
                    return to_enum unless block_given?
         | 
| 24 | 
            -
             | 
| 25 | 
            -
                    while true
         | 
| 26 | 
            -
                      __getobj__.each(&block)
         | 
| 27 | 
            -
             | 
| 28 | 
            -
                      break if last_page?
         | 
| 29 | 
            -
             | 
| 30 | 
            -
                      move_cursor
         | 
| 31 | 
            -
                    end
         | 
| 32 | 
            -
                  end
         | 
| 33 | 
            -
             | 
| 34 | 
            -
                  def cursor_mark
         | 
| 35 | 
            -
                    params[:cursorMark]
         | 
| 36 | 
            -
                  end
         | 
| 37 | 
            -
             | 
| 38 | 
            -
                  def next_cursor_mark
         | 
| 39 | 
            -
                    response[:nextCursorMark]
         | 
| 40 | 
            -
                  end
         | 
| 41 | 
            -
             | 
| 42 | 
            -
                  def last_page?
         | 
| 43 | 
            -
                    cursor_mark == next_cursor_mark
         | 
| 44 | 
            -
                  end
         | 
| 45 | 
            -
             | 
| 46 | 
            -
                  def move_cursor
         | 
| 47 | 
            -
                    __setobj__(next_page)
         | 
| 48 | 
            -
                  end
         | 
| 49 | 
            -
             | 
| 50 | 
            -
                  def next_page
         | 
| 51 | 
            -
                    __getobj__.add_params(cursorMark: next_cursor_mark)
         | 
| 52 | 
            -
                  end
         | 
| 53 | 
            -
             | 
| 54 | 
            -
                end
         | 
| 55 | 
            -
              end
         | 
| 56 | 
            -
            end
         |