{"id":13879465,"url":"https://github.com/djellemah/philtre","last_synced_at":"2025-07-16T15:32:22.406Z","repository":{"id":18212531,"uuid":"21348310","full_name":"djellemah/philtre","owner":"djellemah","description":"It's the Sequel equivalent for Ransack, Metasearch, Searchlogic. If this doesn't make you fall in love, I don't know what will.","archived":false,"fork":false,"pushed_at":"2023-07-21T12:05:46.000Z","size":80,"stargazers_count":19,"open_issues_count":1,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-08-07T08:12:40.764Z","etag":null,"topics":["filter","metasearch","ruby","sequel"],"latest_commit_sha":null,"homepage":null,"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/djellemah.png","metadata":{"files":{"readme":"README.md","changelog":"History.txt","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":"AUTHORS"}},"created_at":"2014-06-30T11:05:15.000Z","updated_at":"2024-05-01T08:58:34.000Z","dependencies_parsed_at":"2024-01-13T20:57:16.800Z","dependency_job_id":"889e36f6-c361-4eaa-9c38-1f2a53a2a4e6","html_url":"https://github.com/djellemah/philtre","commit_stats":{"total_commits":68,"total_committers":1,"mean_commits":68.0,"dds":0.0,"last_synced_commit":"f6c460cb768117fd67af4da48c81f6bf823fa26e"},"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/djellemah%2Fphiltre","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/djellemah%2Fphiltre/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/djellemah%2Fphiltre/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/djellemah%2Fphiltre/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/djellemah","download_url":"https://codeload.github.com/djellemah/philtre/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":226143895,"owners_count":17580245,"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":["filter","metasearch","ruby","sequel"],"created_at":"2024-08-06T08:02:21.933Z","updated_at":"2024-11-24T08:31:23.459Z","avatar_url":"https://github.com/djellemah.png","language":"Ruby","funding_links":[],"categories":["Ruby"],"sub_categories":[],"readme":"# philtre [![Gem Version](https://badge.fury.io/rb/philtre.svg)](http://badge.fury.io/rb/philtre) ![rspecs](https://github.com/djellemah/upl/actions/workflows/build.yml/badge.svg)\n\nIt's the [Sequel](http://sequel.jeremyevans.net) equivalent for Ransack, Metasearch, Searchlogic. If\nthis doesn't make you fall in love, I don't know what will :-p\n\nSee [philtre-rails](http://github.com/djellemah/philtre-rails) for rails integration.\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n    gem 'philtre'\n\nOr for all the rails integration goodies\n\n    gem 'philtre-rails'\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install philtre\n\n## Basic Usage\n\nParse the predicates on the end of field names, and modify a Sequel::Dataset\nto retrieve matching rows.\n\nSo, using a fairly standard rails-style parameter hash:\n\n``` ruby\n  filter_parameters = {\n    birth_year: ['2012', '2011'],\n    title: 'bar',\n    order: ['title', 'name_asc', 'birth_year_desc'],\n  }\n\n  # This would normally be a real Sequel::Dataset\n  personages_dataset = Sequel.mock[:personages]\n\n  philtre = Philtre.new( filter_parameters ).apply( personages_dataset ).sql\n```\n\nshould result in (formatting added here for clarity)\n\n``` SQL\n  SELECT *\n  FROM \"personages\"\n  WHERE\n    ((\"birth_year\" IN ('2012', '2011'))\n    AND\n    (\"title\" = 'bar'))\n  ORDER BY (\"title\" ASC, \"name\" ASC, \"date\" DESC)\n```\n\n## Predicates\n\n```{title: 'sir'}``` is fine when you want to match on string equality. But\nthere are all kinds of other things you need to do. For example\n\n```{title_like: 'sir', age_gt: 10}``` is for a where clause ```title ~* 'sir' and age \u003e  10```\n\nThere are a range of predefined predicates, mostly borrowed from the other search gems:\n\n```\n  gt\n  gte, gteq\n  lt\n  lte, lteq\n  eq\n  not_eq\n  matches, like\n  not_blank\n  like_all\n  like_any\n  null\n  not_null\n```\n\n## Custom Predicates\n\nThere are two ways:\n\n1) You can also define your own by creating a Filter with a block:\n\n``` ruby\n  philtre = Philtre.new filter_parameters do\n    def tagged_by_id(tag_ids)\n      Tag.db[:projects_tags]\n        .select(:personage_id)\n        .filter(tag_id: tag_ids, :project_id =\u003e :personage__id )\n        .exists\n    end\n\n    def really_fancy(tag_ids)\n      # do some really fancy SQL here\n    end\n\n    # etc...\n  end\n```\n\nNow you can pass the filter_parameter hash ```{tagged_by_id: 45}```.\n\nThe result of a predicate block should be a ```Sequel::SQL::Expression``` (ie\none of Sequel's hash expressions in the simplest case) which will work instead\nof its named placeholder. So for example, if the placeholder is inside a SELECT\nclause it wouldn't work to give it an ORDER BY expression.\n\n2) You could also inherit from ```Philtre::Filter``` and override\n```#predicates```. And optionally override ```Philtre.new``` (which is just a\nfactory method on ```module Philtre```) to return the instance of your class.\n\n## Advanced usage\n\nThere is also the ```Philtre::Grinder``` class which can insert placeholders into\nyour ```Sequel::Dataset``` definition, and then substitute those once it has the\nparameter hash. Effectively this makes it a SQL macro engine.\n\nWhy so complicated? Well, it's really handy when you need to use aggregate\nqueries, and apply different values in the parameter hash to where clauses\ninside and the outside of the aggregation. For example, give me a list of all\nstores in a particular region who share of total sales was more than some\npercentage. Yes, you can also use window functions to deal with that\nparticular query.\n\n``` ruby\n  # This would normally be a real Sequel::Dataset\n  stores_dataset = Sequel.mock[:stores]\n\n  # parameterise it with placeholders\n  parameterised_dataset = stores_dataset.filter( :region.lieu, :sales_gt.lieu, :manager.lieu )\n\n  filter_parameters = {\n    region: 'The Bundus',\n    sales_gt: 10,\n    order: ['store_name', 'sales_desc'],\n  }\n\n  # generate the SQL you need\n  parameterised_dataset.grind( Philtre.new( filter_parameters ) ).sql\n```\n\nwill result in\n\n``` SQL\n  SELECT *\n  FROM stores\n  WHERE ((region = 'The Bundus') AND (sales \u003e 10))\n```\n\nNotice that the manager part of the where clause is absent because\nfilter_parameters didn't have a manager key.\n\nLook at the sql generated by parameterised_dataset and you'll see the placeholders\nmarked by SQL comments, so you can debug the Giant SQL Statement more easily. You\nmight also want to find a command-line SQL pretty printer (eg ``fsqlf```) and use it to produce\nreadable SQL instead of a very long hard-to-read string.\n\nIf you don't like the monkey-patching of Symbol with #lieu, you can use\nseveral other ways to generate the placeholders. ```Philtre::PlaceHolder.new```\nis canonical in that all the other possibilities use it.\n\n## Highly Advanced Usage\n\nSometimes method chaining gets ugly. So you can install gem ripar, then you can say\n\n``` ruby\n  store_id_range = 20..90\n  parameterised_dataset = stores_dataset.rolled do\n    where :region.lieu, :sales_gt.lieu, :manager.lieu\n    where store_id: store_id_range\n    select_append db[:products].join(:stores, :store_id =\u003e :id ).select(:product_name)\n  end\n```\n\nNotice that values outside the block are accessible inside, _without_ the need\nfor a block parameter. This uses [Ripar](https://github.com/djellemah/ripar/)\nunder the cover and indirects the binding lookup, so may result in errors that\nyou won't expect.\n\n## Specs\n\nNothing fancy. Just:\n\n    $ rspec spec\n\n## Contributing\n\n1. Fork it ( http://github.com/djellemah/philtre/fork )\n2. Create your feature branch (`git checkout -b my-new-feature`)\n3. Commit your changes (`git commit -am 'Add some feature'`)\n4. Push to the branch (`git push origin my-new-feature`)\n5. Create new Pull Request\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdjellemah%2Fphiltre","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdjellemah%2Fphiltre","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdjellemah%2Fphiltre/lists"}