Class: Queries::Query::Autocomplete
- Inherits:
-
Queries::Query
- Object
- Queries::Query
- Queries::Query::Autocomplete
- Includes:
- Arel::Nodes, Concerns::Identifiers
- Defined in:
- lib/queries/query/autocomplete.rb
Overview
Requires significant refactor.
To consider: In general our optimization follows this pattern:
a: Names that match exactly, full string b: Names that match exactly, full Identifier (cached) c: Names that match start of string exactly (cached), wildcard end of string, minimum 2 characters d: Names that have a very high cuttoff [good wildcard anywhere] ? d.1: Names that have wildcard either side (limit to 2 characters). Are results optimally better than d? e: Names that have exact ID (internal) (will come to top automatically) f: Names that match some special pattern (e.g. First letter, second name in taxon name search). These may need higher priority in the stack.
May also consider length, priority, similarity
Direct Known Subclasses
AnatomicalPart::Autocomplete, AssertedDistribution::Autocomplete, AssertedEnvironment::Autocomplete, BiologicalAssociation::Autocomplete, BiologicalAssociationsGraph::Autocomplete, BiologicalRelationship::Autocomplete, CollectingEvent::Autocomplete, CollectionObject::Autocomplete, Container::Autocomplete, ControlledVocabularyTerm::Autocomplete, Conveyance::Autocomplete, DataAttribute::Autocomplete, DataAttribute::ValueAutocomplete, Depiction::Autocomplete, Descriptor::Autocomplete, Document::Autocomplete, Extract::Autocomplete, FieldOccurrence::Autocomplete, Gazetteer::Autocomplete, GeographicArea::Autocomplete, Identifier::Autocomplete, Image::Autocomplete, Lead::Autocomplete, Loan::Autocomplete, Namespace::Autocomplete, Note::Autocomplete, Observation::Autocomplete, ObservationMatrixRow::Autocomplete, Organization::Autocomplete, Otu::Autocomplete, Person::Autocomplete, PreparationType::Autocomplete, Serial::Autocomplete, Sound::Autocomplete, Source::Autocomplete, TaxonName::Autocomplete, TaxonName::Tabular, TypeMaterial::Autocomplete, User::Autocomplete
Constant Summary collapse
- DEFAULT_LIMIT =
The default #limit. Subclasses whose #autocomplete honours #limit set their own.
20- DELEGATED_DEPTH =
How deep a restricted #delegated_autocomplete takes its inner autocomplete
5000- DELEGATED_MINIMUM_LENGTH =
The shortest query string a restricted #delegated_autocomplete runs its inner autocomplete for
3- LITERAL_RESTRICTION_MAX =
The most ids #asserted_object_restriction passes literally
1000
Instance Attribute Summary collapse
-
#delegated_results ⇒ Hash?
Optional, a store for the inner results of restricted #delegated_autocomplete calls.
- #dynamic_limit ⇒ Integer
-
#limit ⇒ Integer
The maximum number of results #autocomplete returns, the given limit or the subclass' DEFAULT_LIMIT.
- #project_id ⇒ Array
-
#query_string ⇒ String?
The initial, unparsed value, sanitized.
-
#restrict_to ⇒ ActiveRecord::Relation?
Optional, restricts results to these records of referenced_klass.
Attributes inherited from Queries::Query
Instance Method Summary collapse
-
#apply_restriction(query) ⇒ ActiveRecord::Relation
Apply #restrict_to to query.
-
#asserted_object_autocomplete(object_type:, object_autocomplete_class:, object_association:) ⇒ ActiveRecord::Relation?
For autocompletes of records asserting something about a polymorphic object (e.g. AssertedDistribution, AssertedEnvironment): match the objects of
object_typewith their own autocomplete, then return the asserted records of those objects, in the object autocomplete's rank order. -
#asserted_object_restriction(asserted, object_klass, object_id_column) ⇒ ActiveRecord::Relation?
The object_klass records that have an
assertedrecord, nil if there are none. -
#autocomplete ⇒ Array
Default the autocomplete result to all TODO: eliminate.
- #autocomplete_cached ⇒ ActiveRecord::Relation
-
#autocomplete_cached_wildcard_anywhere ⇒ ActiveRecord::Relation
Removes years/integers!.
- #autocomplete_common_name_exact ⇒ Object
-
#autocomplete_common_name_like ⇒ Object
TODO: GIN/similarity.
- #autocomplete_exact_id ⇒ ActiveRecord::Relation
- #autocomplete_exactly_named ⇒ ActiveRecord::Relation
- #autocomplete_named ⇒ ActiveRecord::Relation
- #autocomplete_ordered_wildcard_pieces_in_cached ⇒ ActiveRecord::Relation
-
#cached_facet ⇒ ActiveRecord::Relation?
TODO: Used in taxon_name, source, identifier.
- #combine_or_clauses(clauses) ⇒ Arel::Nodes::Grouping
- #common_name_name ⇒ Object
- #common_name_table ⇒ Object
- #common_name_wild_pieces ⇒ Object
-
#delegated_autocomplete(build:, keep:, key:) ⇒ Array
Run another model's (inner) autocomplete on behalf of this one.
- #exactly_named ⇒ Arel::Nodes::Matches
-
#fragments ⇒ Array
Used in unordered AND searches.
-
#initialize(string, project_id: nil, restrict_to: nil, limit: nil, **keyword_args) ⇒ Autocomplete
constructor
A new instance of Autocomplete.
-
#integers ⇒ Array
Of strings representing integers.
-
#least_levenshtein(fields, value) ⇒ Object
Calculate the levenshtein distance for a value across multiple columns, and keep the smallest.
-
#match_wildcard_end_in_cached ⇒ Arel::Nodes::Matches
match ALL wildcards, but unordered, if 2 - 6 pieces provided.
-
#match_wildcard_in_cached ⇒ Arel::Nodes::Matches
match ALL wildcards, but unordered, if 2 - 6 pieces provided.
- #named ⇒ Arel::Nodes::Matches
-
#only_ids ⇒ Arek::Npdes?
Used in or_clauses, match on id only if integers alone provided.
- #only_integers? ⇒ Boolean
-
#order_by_id_rank(query, column, ids) ⇒ ActiveRecord::Relation
Order query by the position of
columninids, e.g. -
#parent ⇒ Arel::Nodes::TableAlias
Used in heirarchy joins.
- #parent_child_join ⇒ Scope
-
#parent_child_where ⇒ Arel::Nodes::Grouping
Match at two levels, for example, 'wa te" will match "Washington Co., Texas".
-
#pieces ⇒ Array
TODO: used?!.
-
#safe_integers ⇒ Array<Integer>
Array of integers parsed from
query_stringthat fit within the 4-byte SQL integer range (1 to 2_147_483_647). -
#scope ⇒ Scope
stub TODO: deprecate? probably unused.
-
#string_fragments ⇒ Array
Used in unordered AND searches.
- #wildcard_wrapped_integers ⇒ Array
- #wildcard_wrapped_years ⇒ Array
- #with_cached ⇒ Arel::Nodes::Matches
- #with_cached_like ⇒ Arel::Nodes::Matches
-
#with_id ⇒ Arel::Nodes?
Used in or_clauses.
-
#with_project_id ⇒ Arel::Nodes::Equality
TODO: nil/or clause this.
- #year_letter ⇒ String?
- #years ⇒ Array
Methods inherited from Queries::Query
#alphabetic_strings, #alphanumeric_strings, base_name, #base_name, #base_query, #build_terms, #end_wildcard, #levenshtein_distance, #match_ordered_wildcard_pieces_in_cached, #no_terms?, referenced_klass, #referenced_klass, #referenced_klass_except, #referenced_klass_intersection, #referenced_klass_union, #start_and_end_wildcard, #start_wildcard, #table, #wildcard_pieces
Constructor Details
#initialize(string, project_id: nil, restrict_to: nil, limit: nil, **keyword_args) ⇒ Autocomplete
Returns a new instance of Autocomplete.
67 68 69 70 71 72 73 74 75 76 |
# File 'lib/queries/query/autocomplete.rb', line 67 def initialize(string, project_id: nil, restrict_to: nil, limit: nil, **keyword_args) @query_string = ::ApplicationRecord.sanitize_sql(string)&.delete("\u0000") # remove null bytes @project_id = project_id @restrict_to = restrict_to @limit = limit # should not need this # build_terms # TODO - should remove this for accessors end |
Instance Attribute Details
#delegated_results ⇒ Hash?
Returns optional, a store for the inner results of restricted #delegated_autocomplete calls. An autocomplete that creates several autocompletes delegating to the same inner autocomplete (e.g. one per side of a biological association) gives them all the same Hash, so the DELEGATED_DEPTH fetch runs once. nil (default) is no sharing.
64 65 66 |
# File 'lib/queries/query/autocomplete.rb', line 64 def delegated_results @delegated_results end |
#dynamic_limit ⇒ Integer
34 35 36 |
# File 'lib/queries/query/autocomplete.rb', line 34 def dynamic_limit @dynamic_limit end |
#limit ⇒ Integer
Returns the maximum number of results #autocomplete returns, the given limit or the subclass' DEFAULT_LIMIT.
85 86 87 |
# File 'lib/queries/query/autocomplete.rb', line 85 def limit @limit || self.class::DEFAULT_LIMIT end |
#project_id ⇒ Array
26 27 28 |
# File 'lib/queries/query/autocomplete.rb', line 26 def project_id @project_id end |
#query_string ⇒ String?
Returns the initial, unparsed value, sanitized.
30 31 32 |
# File 'lib/queries/query/autocomplete.rb', line 30 def query_string @query_string end |
#restrict_to ⇒ ActiveRecord::Relation?
Returns optional, restricts results to these records of referenced_klass.
nil (default) is no restriction. Must be a relation of
referenced_klass (or a subclass), e.g. ::CollectingEvent.where(...);
any select on it is replaced with select(:id). Anything else raises
ArgumentError. See #apply_restriction.
45 46 47 |
# File 'lib/queries/query/autocomplete.rb', line 45 def restrict_to @restrict_to end |
Instance Method Details
#apply_restriction(query) ⇒ ActiveRecord::Relation
Apply #restrict_to to query. Subclasses should call this where they assemble their individual queries (i.e. alongside where project_id is applied), rather than in base_query, so that it covers queries not built from base_query (e.g. referenced_klass.joins(:identifiers)).
Use this when the caller only wants a subset of the model's records, typically when one autocomplete delegates to another: restricting the inner autocomplete to candidates the outer one can use keeps it fast, and keeps unusable candidates from filling its result limits.
The restriction is table.id IN (<restrict_to>). Requiring a relation
of referenced_klass means the ids are always the model's own, and
reselect(:id) guarantees the single column an IN subquery needs.
105 106 107 108 109 110 111 112 113 114 115 116 |
# File 'lib/queries/query/autocomplete.rb', line 105 def apply_restriction(query) return query if restrict_to.nil? r = restrict_to unless r.is_a?(ActiveRecord::Relation) && r.klass <= referenced_klass given = r.is_a?(ActiveRecord::Relation) ? r.klass.name : r.class.name raise ArgumentError, "restrict_to must be a relation of #{referenced_klass.name}, not #{given}" end query.where(table[:id].in(r.reselect(:id).arel)) end |
#asserted_object_autocomplete(object_type:, object_autocomplete_class:, object_association:) ⇒ ActiveRecord::Relation?
For autocompletes of records asserting something about a polymorphic
object (e.g. AssertedDistribution, AssertedEnvironment): match the
objects of object_type with their own autocomplete, then return the
asserted records of those objects, in the object autocomplete's rank
order.
The combinatorics are not great for joining each object autocomplete option with the asserted records directly, so the object autocomplete is run restricted to objects that have asserted records (in #project_id, within #restrict_to). This keeps it fast and keeps objects without (usable) asserted records from filling its result limit: every one has one, so #limit of them can fill the results.
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 |
# File 'lib/queries/query/autocomplete.rb', line 247 def asserted_object_autocomplete(object_type:, object_autocomplete_class:, object_association:) type_column = "#{object_association}_type" id_column = "#{object_association}_id" asserted = referenced_klass.where(type_column => object_type) asserted = asserted.where(project_id:) if project_id.present? asserted = apply_restriction(asserted) objects = asserted_object_restriction(asserted, object_type.constantize, id_column) return nil if objects.nil? object_ids = object_autocomplete_class .new(query_string, project_id:, restrict_to: objects, limit:) .autocomplete.map(&:id) return nil if object_ids.empty? order_by_id_rank( asserted.where(id_column => object_ids), "#{table.name}.#{id_column}", object_ids ).limit(limit) end |
#asserted_object_restriction(asserted, object_klass, object_id_column) ⇒ ActiveRecord::Relation?
Returns the object_klass records that have an asserted record, nil if
there are none.
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 |
# File 'lib/queries/query/autocomplete.rb', line 207 def asserted_object_restriction(asserted, object_klass, object_id_column) # The restriction is re-applied in every query of the object # autocomplete, and of those it delegates to (e.g. BA -> Otu/CO/... -> # TaxonName, dozens of queries): when there are few ids, passing them # literally is much cheaper than re-evaluating the subquery in each; # when there are many, the subquery is cheaper. Only the caller can # tell cheaply (one pluck here); checking the size at every level of # the chain costs more than it saves. ids = asserted.distinct .limit(LITERAL_RESTRICTION_MAX + 1).pluck(object_id_column) return nil if ids.empty? if ids.size <= LITERAL_RESTRICTION_MAX object_klass.where(id: ids) else object_klass.where(id: asserted.select(object_id_column)) end end |
#autocomplete ⇒ Array
Returns default the autocomplete result to all TODO: eliminate.
460 461 462 463 |
# File 'lib/queries/query/autocomplete.rb', line 460 def autocomplete return [] if query_string.blank? all.to_a end |
#autocomplete_cached ⇒ ActiveRecord::Relation
498 499 500 501 502 503 504 |
# File 'lib/queries/query/autocomplete.rb', line 498 def autocomplete_cached if a = cached_facet base_query.where(a.to_sql).limit(20) else nil end end |
#autocomplete_cached_wildcard_anywhere ⇒ ActiveRecord::Relation
Returns removes years/integers!.
482 483 484 485 486 |
# File 'lib/queries/query/autocomplete.rb', line 482 def autocomplete_cached_wildcard_anywhere a = match_wildcard_in_cached return nil if a.nil? base_query.where(a.to_sql) end |
#autocomplete_common_name_exact ⇒ Object
530 531 532 533 |
# File 'lib/queries/query/autocomplete.rb', line 530 def autocomplete_common_name_exact return nil if no_terms? base_query.joins(:common_names).where(common_name_name.to_sql).limit(1) end |
#autocomplete_common_name_like ⇒ Object
TODO: GIN/similarity
536 537 538 539 |
# File 'lib/queries/query/autocomplete.rb', line 536 def autocomplete_common_name_like return nil if no_terms? base_query.joins(:common_names).where(common_name_wild_pieces.to_sql).limit(5) end |
#autocomplete_exact_id ⇒ ActiveRecord::Relation
466 467 468 469 470 471 472 |
# File 'lib/queries/query/autocomplete.rb', line 466 def autocomplete_exact_id if i = ::Utilities::Strings::only_integer(query_string) base_query.where(id: i).limit(1) else nil end end |
#autocomplete_exactly_named ⇒ ActiveRecord::Relation
507 508 509 510 |
# File 'lib/queries/query/autocomplete.rb', line 507 def autocomplete_exactly_named return nil if no_terms? base_query.where(exactly_named.to_sql).limit(20) end |
#autocomplete_named ⇒ ActiveRecord::Relation
513 514 515 516 |
# File 'lib/queries/query/autocomplete.rb', line 513 def autocomplete_named return nil if no_terms? base_query.where(named.to_sql).limit(20) end |
#autocomplete_ordered_wildcard_pieces_in_cached ⇒ ActiveRecord::Relation
475 476 477 478 |
# File 'lib/queries/query/autocomplete.rb', line 475 def autocomplete_ordered_wildcard_pieces_in_cached return nil if no_terms? base_query.where(match_ordered_wildcard_pieces_in_cached.to_sql) end |
#cached_facet ⇒ ActiveRecord::Relation?
TODO: Used in taxon_name, source, identifier
491 492 493 494 495 |
# File 'lib/queries/query/autocomplete.rb', line 491 def cached_facet return nil if no_terms? # TODO: or is redundant with terms in many cases (table[:cached].matches_any(terms)).or(match_ordered_wildcard_pieces_in_cached) end |
#combine_or_clauses(clauses) ⇒ Arel::Nodes::Grouping
442 443 444 445 446 447 448 449 450 |
# File 'lib/queries/query/autocomplete.rb', line 442 def combine_or_clauses(clauses) clauses.compact! raise TaxonWorks::Error, 'combine_or_clauses called without a clause, ensure at least one exists' unless !clauses.empty? a = clauses.shift clauses.each do |b| a = a.or(b) end a end |
#common_name_name ⇒ Object
522 523 524 |
# File 'lib/queries/query/autocomplete.rb', line 522 def common_name_name common_name_table[:name].eq(query_string) end |
#common_name_table ⇒ Object
518 519 520 |
# File 'lib/queries/query/autocomplete.rb', line 518 def common_name_table ::CommonName.arel_table end |
#common_name_wild_pieces ⇒ Object
526 527 528 |
# File 'lib/queries/query/autocomplete.rb', line 526 def common_name_wild_pieces common_name_table[:name].matches(wildcard_pieces) end |
#delegated_autocomplete(build:, keep:, key:) ⇒ Array
Run another model's (inner) autocomplete on behalf of this one. Use it when an autocomplete delegates to another, instead of translating #restrict_to for the inner model.
Unrestricted, the inner autocomplete is simply run, #limit deep.
Restricted, translating the restriction for the inner model is
relation-specific and easy to get subtly wrong (e.g. Otus -> the
TaxonNames, and Combinations, they resolve to), and pushing it into
each of the inner autocomplete's queries is expensive for large
restrictions (it's re-built in each query), so instead the inner
autocomplete is run once,
unrestricted, DELEGATED_DEPTH deep, and its results filtered by keep
(one query). Best effort: usable matches ranked below DELEGATED_DEPTH
(the least relevant) are missed. A LIMIT costs nothing for terms that
match fewer rows, so only broad terms pay for the depth; one deep fetch
was faster overall than starting shallower and deepening.
Restricted, query strings shorter than DELEGATED_MINIMUM_LENGTH return no inner results: they match so broadly that the DELEGATED_DEPTH fetched are an arbitrary sample (largely unordered wildcard matches), and the slowest to fetch.
The DELEGATED_DEPTH results depend only on the inner autocomplete, not
on #restrict_to, so autocompletes given the same #delegated_results
share them, by key.
The inner autocomplete must accept limit: and give its queries that
can return many rows a ranking order, so that the first
DELEGATED_DEPTH are the best ranked (see lib/queries/ARCHITECTURE.md).
So far only TaxonName autocomplete is used this way (its
#autocomplete_wildcard_joined_strings is unordered).
170 171 172 173 174 175 176 177 178 |
# File 'lib/queries/query/autocomplete.rb', line 170 def delegated_autocomplete(build:, keep:, key:) inner = build.call(limit) return inner.autocomplete if restrict_to.nil? return [] if query_string.to_s.length < DELEGATED_MINIMUM_LENGTH inner.limit = DELEGATED_DEPTH results = (delegated_results || {})[[inner.class, *key]] ||= inner.autocomplete keep.call(results).first(limit) end |
#exactly_named ⇒ Arel::Nodes::Matches
397 398 399 |
# File 'lib/queries/query/autocomplete.rb', line 397 def exactly_named table[:name].eq(query_string) if query_string.present? end |
#fragments ⇒ Array
Used in unordered AND searches
308 309 310 311 312 313 314 315 |
# File 'lib/queries/query/autocomplete.rb', line 308 def fragments a = alphanumeric_strings if a.size > 0 && a.size < 6 a.collect{|a| "%#{a}%"} else [] end end |
#integers ⇒ Array
Returns of strings representing integers.
287 288 289 |
# File 'lib/queries/query/autocomplete.rb', line 287 def integers Utilities::Strings.integers(query_string) end |
#least_levenshtein(fields, value) ⇒ Object
Calculate the levenshtein distance for a value across multiple columns, and keep the smallest.
545 546 547 548 |
# File 'lib/queries/query/autocomplete.rb', line 545 def least_levenshtein(fields, value) levenshtein_sql = fields.map {|f| levenshtein_distance(f, value).to_sql } Arel.sql("least(#{levenshtein_sql.join(", ")})") end |
#match_wildcard_end_in_cached ⇒ Arel::Nodes::Matches
match ALL wildcards, but unordered, if 2 - 6 pieces provided
429 430 431 |
# File 'lib/queries/query/autocomplete.rb', line 429 def match_wildcard_end_in_cached table[:cached].matches(end_wildcard) end |
#match_wildcard_in_cached ⇒ Arel::Nodes::Matches
match ALL wildcards, but unordered, if 2 - 6 pieces provided
435 436 437 438 439 |
# File 'lib/queries/query/autocomplete.rb', line 435 def match_wildcard_in_cached b = fragments return nil if b.empty? table[:cached].matches_all(b) end |
#named ⇒ Arel::Nodes::Matches
392 393 394 |
# File 'lib/queries/query/autocomplete.rb', line 392 def named table[:name].matches_any(terms) if terms.any? end |
#only_ids ⇒ Arek::Npdes?
Returns used in or_clauses, match on id only if integers alone provided.
383 384 385 386 387 388 389 |
# File 'lib/queries/query/autocomplete.rb', line 383 def only_ids if only_integers? with_id else nil end end |
#only_integers? ⇒ Boolean
301 302 303 |
# File 'lib/queries/query/autocomplete.rb', line 301 def only_integers? Utilities::Strings.only_integers?(query_string) end |
#order_by_id_rank(query, column, ids) ⇒ ActiveRecord::Relation
Order query by the position of column in ids, e.g. another
autocomplete's ranked results, so that a limit keeps the best matches.
Ties (e.g. several records per id) are broken by #table id.
190 191 192 193 194 |
# File 'lib/queries/query/autocomplete.rb', line 190 def order_by_id_rank(query, column, ids) query.order(Arel.sql( "array_position(ARRAY[#{ids.map(&:to_i).join(',')}], #{column}), #{table.name}.id" )) end |
#parent ⇒ Arel::Nodes::TableAlias
Returns used in heirarchy joins.
403 404 405 |
# File 'lib/queries/query/autocomplete.rb', line 403 def parent table.alias end |
#parent_child_join ⇒ Scope
359 360 361 |
# File 'lib/queries/query/autocomplete.rb', line 359 def parent_child_join table.join(parent).on(table[:parent_id].eq(parent[:id])).join_sources end |
#parent_child_where ⇒ Arel::Nodes::Grouping
Match at two levels, for example, 'wa te" will match "Washington Co., Texas"
365 366 367 368 369 |
# File 'lib/queries/query/autocomplete.rb', line 365 def parent_child_where a,b = query_string.split(/\s+/, 2) return table[:id].eq(-1) if a.nil? || b.nil? table[:name].matches("#{a}%").and(parent[:name].matches("#{b}%")) end |
#pieces ⇒ Array
TODO: used?!
332 333 334 |
# File 'lib/queries/query/autocomplete.rb', line 332 def pieces query_string.split(/\s+/) end |
#safe_integers ⇒ Array<Integer>
Returns Array of integers parsed from query_string that fit within the
4-byte SQL integer range (1 to 2_147_483_647).
294 295 296 297 298 |
# File 'lib/queries/query/autocomplete.rb', line 294 def safe_integers integers .map(&:to_i) .select { |i| i.between?(1, 2_147_483_647) } end |
#scope ⇒ Scope
stub TODO: deprecate? probably unused
271 272 273 |
# File 'lib/queries/query/autocomplete.rb', line 271 def scope where('1 = 2') end |
#string_fragments ⇒ Array
Used in unordered AND searches
320 321 322 323 324 325 326 327 |
# File 'lib/queries/query/autocomplete.rb', line 320 def string_fragments a = alphabetic_strings if a.size > 0 && a.size < 6 a.collect{|a| "%#{a}%"} else [] end end |
#wildcard_wrapped_integers ⇒ Array
337 338 339 |
# File 'lib/queries/query/autocomplete.rb', line 337 def wildcard_wrapped_integers integers.collect{|i| "%#{i}%"} end |
#wildcard_wrapped_years ⇒ Array
342 343 344 |
# File 'lib/queries/query/autocomplete.rb', line 342 def wildcard_wrapped_years years.collect{|i| "%#{i}%"} end |
#with_cached ⇒ Arel::Nodes::Matches
418 419 420 |
# File 'lib/queries/query/autocomplete.rb', line 418 def with_cached table[:cached].eq(query_string) end |
#with_cached_like ⇒ Arel::Nodes::Matches
423 424 425 |
# File 'lib/queries/query/autocomplete.rb', line 423 def with_cached_like table[:cached].matches(start_and_end_wildcard) end |
#with_id ⇒ Arel::Nodes?
Returns used in or_clauses.
373 374 375 376 377 378 379 |
# File 'lib/queries/query/autocomplete.rb', line 373 def with_id if safe_integers.any? table[:id].in(safe_integers) else nil end end |
#with_project_id ⇒ Arel::Nodes::Equality
TODO: nil/or clause this
409 410 411 412 413 414 415 |
# File 'lib/queries/query/autocomplete.rb', line 409 def with_project_id if project_id.present? table[:project_id].in(project_id) else nil end end |
#year_letter ⇒ String?
281 282 283 |
# File 'lib/queries/query/autocomplete.rb', line 281 def year_letter Utilities::Strings.year_letter(query_string) end |