{"id":28645985,"url":"https://github.com/boblail/hayfork","last_synced_at":"2025-07-25T21:13:53.267Z","repository":{"id":24058295,"uuid":"27444279","full_name":"boblail/hayfork","owner":"boblail","description":"Full-Text search for ActiveRecord and Postgres","archived":false,"fork":false,"pushed_at":"2022-02-16T15:47:11.000Z","size":42,"stargazers_count":4,"open_issues_count":3,"forks_count":4,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-05-26T13:55:09.817Z","etag":null,"topics":["activerecord","fulltext-search","postgres","ruby","search","tsearch"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/boblail.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-12-02T17:28:17.000Z","updated_at":"2021-12-01T15:48:30.000Z","dependencies_parsed_at":"2022-08-20T23:10:24.095Z","dependency_job_id":null,"html_url":"https://github.com/boblail/hayfork","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/boblail/hayfork","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/boblail%2Fhayfork","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/boblail%2Fhayfork/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/boblail%2Fhayfork/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/boblail%2Fhayfork/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/boblail","download_url":"https://codeload.github.com/boblail/hayfork/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/boblail%2Fhayfork/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259564114,"owners_count":22877226,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["activerecord","fulltext-search","postgres","ruby","search","tsearch"],"created_at":"2025-06-13T01:42:52.219Z","updated_at":"2025-06-13T01:43:40.165Z","avatar_url":"https://github.com/boblail.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Hayfork\n\n[![Gem Version](https://badge.fury.io/rb/hayfork.svg)](https://rubygems.org/gems/hayfork)\n[![Build Status](https://travis-ci.org/boblail/hayfork.svg)](https://travis-ci.org/boblail/hayfork)\n\nFull-Text search for ActiveRecord and Postgres.\n\nHayfork generates triggers to maintain a **Haystack** of all searchable fields that Postgres can index easily and efficiently.\n\n\n\n\u003cbr/\u003e\n\n## About\n\n#### How Hayfork works\n\nYou define the tables and fields that are to be searchable. Hayfork defines triggers that watch those tables for INSERTs, UPDATEs, and DELETEs. In response, the triggers insert, update, or delete corresponding rows in the haystack: one row per searchable field.\n\nThey Haystack has a column named `search_vector` that can be indexed, optimizing searches.\n\nA query against the Haystack returns a list of **hits** — one result may have more than one hit (as when a search string is found in both the text and title of a book).\n\n\n#### Why Hayfork?\n\nHayfork is designed to:\n\n - optimize searches by:\n    - executing one query to search any number of fields or tables\n    - writing `search_vector` when hits are inserted so that the column may be indexed\n - rebuild the haystack at the database level so that it works with bulk-inserted records\n - support extension so, by adding metadata to a hit, you can:\n    - provide additional context about a result in the UI\n    - search only within a particular field (e.g. enable users to search `author:Potok` to find books whether the author's name includes \"Potok\")\n    - scope searches by a user or tenant or feature\n\n\n\u003cbr/\u003e\n\n## Setup\n\nGenerate a haystack table and model for your application.\n\n    $ rails generate hayfork:haystack\n\nThis will generate several files:\n\n - `app/models/haystack.rb` and `db/migrate/000_create_haystack.rb` define the Haystack\n - `app/models/query.rb` (and several models in the `Query` namespace) are responsible for parsing a query string and constructing the SQL to execute it.\n - `lib/haystack_triggers.rb` is where you will define the tables and fields to be added to the Haystack.\n\n\n\u003cbr/\u003e\n\n## lib/haystack_triggers.rb\n\n#### Basic Example\n\nThis basic example allows you to search all your employees and projects with one search box:\n\n```ruby\nHayfork.maintain(Haystack) do\n  foreach(Employee) do\n    insert(:full_name)\n  end\n  foreach(Project) do\n    insert(:title)\n  end\nend\n```\n\n\u003cbr/\u003e\n\n#### Multiple Fields\n\nTo allow finding employees by multiple traits (e.g by name, job title, or short biography), you can define multiple `insert` statements per employee:\n\n```ruby\nHayfork.maintain(Haystack) do\n  foreach(Employee) do\n    insert(:full_name)\n    insert(:position)\n    insert(:short_bio)\n  end\nend\n```\n\n\u003cbr/\u003e\n\n#### Scoping Searches\n\nAdditional columns on `haystack` can also be useful for scoping searches. Suppose we're maintaining a database of employees for multiple companies. We would want to scope searches by company. If we've added `company_id` to our haystack, we can populate it like this:\n\n```ruby\nHayfork.maintain(Haystack) do\n  foreach(Employee) do\n    set :company_id, row[:company_id]\n\n    insert(:full_name)\n    insert(:position)\n    insert(:short_bio)\n  end\nend\n```\n\nIn this line,\n\n```ruby\n    set :company_id, row[:company_id]\n```\n\n1. `row` is an instance of `Arel::Table` that represents the row passed to the trigger; `row` is present in every `foreach` block.\n2. `set` assigns a value that will be inserted in the haystack for all following `insert` statements.\n\n\u003cbr/\u003e\n\n#### belongs_to\n\nIf a book `belongs_to :author`, you can find the book by _either_ its title or its author's name like this:\n\n```ruby\nHayfork.maintain(Haystack) do\n  foreach(Book) do\n    insert(:title)\n    insert(author: :name)\n  end\nend\n```\n\nWhen a book is inserted, this will add an entry to the haystack for the book's title and another entry for its author's name. If `book.author_id` is changed, it'll replace the appropriate entry in the haystack; but what if `authors.name` is modified? We also need to watch the `authors` table for changes to modify the haystack:\n\n```ruby\nHayfork.maintain(Haystack) do\n  foreach(Book) do\n    insert(:title)\n    insert(author: :name)\n  end\n  foreach(Author) do\n    joins :books\n    set :search_result_type, \"Book\"\n    set :search_result_id, Book.arel_table[:id]\n\n    insert(:name)\n  end\nend\n```\n\nIn the examples seen before, we haven't set `search_result_type` and `search_result_id`. If these values aren't defined, Hayfork assumes that the model passed to `foreach` — the table being watched — is the search result; but for an associated record, we need to explicitly declare the result. In this case, an entry is added to the haystack for every book that belongs to an author.\n\n\u003cbr/\u003e\n\n#### has_many\n\n`has_many` and `has_many :through` associations work much the same way. If an article `has_many :comments`, you can find an article by any of its comments like this:\n\n```ruby\nHayfork.maintain(Haystack) do\n  foreach(Article) do\n    insert(comments: :text)\n  end\n  foreach(Comment) do\n    joins :article\n    set :search_result_type, \"Article\"\n    set :search_result_id, Article.arel_table[:id]\n\n    insert(:text)\n  end\nend\n```\n\n\u003cbr/\u003e\n\n#### Rebuild Triggers\n\nAfter making changes to `lib/haystack_triggers.rb` or to the default scopes of any of the models being used by the Triggers File, you'll need to replace the triggers in your database and rebuild the Haystack. Hayfork generates a migration to do that:\n\n    $ rails generate hayfork:rebuild\n\n\n\n\u003cbr/\u003e\n\n## Development\n\nAfter checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.\n\nTo install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).\n\n\u003cbr/\u003e\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/boblail/hayfork.\n\n\u003cbr/\u003e\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fboblail%2Fhayfork","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fboblail%2Fhayfork","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fboblail%2Fhayfork/lists"}