{"id":13481390,"url":"https://github.com/outr/lucene4s","last_synced_at":"2025-04-07T05:13:11.781Z","repository":{"id":48613111,"uuid":"70624415","full_name":"outr/lucene4s","owner":"outr","description":"Light-weight convenience wrapper around Lucene to simplify complex tasks and add Scala sugar.","archived":false,"fork":false,"pushed_at":"2025-03-23T00:28:28.000Z","size":243,"stargazers_count":53,"open_issues_count":5,"forks_count":19,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-03-31T04:06:02.399Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Scala","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/outr.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-10-11T18:40:58.000Z","updated_at":"2025-02-01T15:45:48.000Z","dependencies_parsed_at":"2025-02-28T23:09:58.038Z","dependency_job_id":"79a2a4b8-bcd6-4efc-b1eb-7c6dba1e74e1","html_url":"https://github.com/outr/lucene4s","commit_stats":null,"previous_names":[],"tags_count":30,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/outr%2Flucene4s","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/outr%2Flucene4s/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/outr%2Flucene4s/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/outr%2Flucene4s/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/outr","download_url":"https://codeload.github.com/outr/lucene4s/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247595335,"owners_count":20963943,"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":[],"created_at":"2024-07-31T17:00:51.426Z","updated_at":"2025-04-07T05:13:11.751Z","avatar_url":"https://github.com/outr.png","language":"Scala","funding_links":[],"categories":["Database","Table of Contents"],"sub_categories":["Database"],"readme":"# lucene4s\n\n[![Build Status](https://travis-ci.org/outr/lucene4s.svg?branch=master)](https://travis-ci.org/outr/lucene4s)\n[![Stories in Ready](https://badge.waffle.io/outr/lucene4s.png?label=ready\u0026title=Ready)](https://waffle.io/outr/lucene4s)\n[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/outr/lucene4s)\n[![Maven Central](https://img.shields.io/maven-central/v/com.outr/lucene4s_2.12.svg)](https://maven-badges.herokuapp.com/maven-central/com.outr/lucene4s_2.12)\n\nLight-weight convenience wrapper around Lucene to simplify complex tasks and add Scala sugar.\n\n## Setup\n\nlucene4s is published to Sonatype OSS and Maven Central currently supporting Scala 2.11, 2.12, 2.13, and 3.0.\n\nConfiguring the dependency in SBT simply requires:\n\n```\nlibraryDependencies += \"com.outr\" %% \"lucene4s\" % \"1.11.1\"\n```\n\n## Using\n\n### Imports\n\nYou may find yourself needing other imports depending on what you're doing, but the majority of functionality can be\nachieved simply importing `com.outr.lucene4s._`:\n\n```scala\nimport com.outr.lucene4s._\n```\n\n### Creating a Lucene Instance\n\n`Lucene` is the object utilized for doing anything with Lucene, so you first need to instantiate it:\n\n```scala\nval directory = Paths.get(\"index\")\nval lucene = new DirectLucene(Nil, directory = Option(directory))\n```\n\nNOTE: If you leave `directory` blank or set it to None (the default) it will use an in-memory index. \n\n### Creating Fields\n\nFor type-safety and convenience we can create the fields we'll be using in the document ahead of time:\n\n```scala\nval name = lucene.create.field[String](\"name\")\nval address = lucene.create.field[String](\"address\")\n```\n\n### Inserting Documents\n\nInserting is quite easy using the document builder:\n\n```scala\nlucene.doc().fields(name(\"John Doe\"), address(\"123 Somewhere Rd.\")).index()\nlucene.doc().fields(name(\"Jane Doe\"), address(\"123 Somewhere Rd.\")).index()\n```\n\n### Querying Documents\n\nQuerying documents is just as easy with the query builder:\n\n```scala\nval paged = lucene.query().sort(Sort(name)).search()\npaged.results.foreach { searchResult =\u003e\n  println(s\"Name: ${searchResult(name)}, Address: ${searchResult(address)}\")\n}\n```\n\nThis will return a `PagedResults` instance with the page size set to the `limit`. There are convenience methods for\nnavigating the pagination and accessing the results.\n\nThe above code will output:\n\n```\nName: John Doe, Address: 123 Somewhere Rd.\nName: Jane Doe, Address: 123 Somewhere Rd.\n```\n\n### Highlighting Results\n\nThough querying is nice, we may want to stylize the output to show the matched results. This is pretty simple:\n\n```scala\nval paged = lucene.query().sort(Sort(name)).filter(fuzzy(name(\"jhn\"))).highlight().search()\npaged.results.foreach { searchResult =\u003e\n  val highlighting = searchResult.highlighting(name).head\n  println(s\"Fragment: ${highlighting.fragment}, Word: ${highlighting.word}\")\n}\n```\n\nThe above code will output:\n\n```\nFragment: \u003cem\u003eJohn\u003c/em\u003e Doe, Word: John\nFragment: \u003cem\u003eJane\u003c/em\u003e Doe, Word: Jane\n```\n\n### Faceted Searching\n\nSee https://github.com/outr/lucene4s/blob/master/implementation/src/test/scala/tests/FacetsSpec.scala\n\n### Full-Text Searching\n\nIn lucene4s the `Lucene` instance holds a `fullText` `Field` that contains a concatenation of all the fields that\nare configured as `fullTextSearchable`.  This defaults to `Lucene.defaultFullTextSearchable` which defaults to false.\n\nThe `fullText` field is the default field used for searches if it's not specified in the `SearchTerm`. Let's see an example:\n\n```\nval paged = lucene.query().filter(wildcard(\"doe*\")).search()\npaged.total should be(4)\npaged.results(0)(firstName) should be(\"John\")\npaged.results(1)(firstName) should be(\"Jane\")\npaged.results(2)(firstName) should be(\"Baby\")\npaged.results(3)(firstName) should be(\"James\")\n```\n\nFor a complete example, see: https://github.com/outr/lucene4s/blob/master/implementation/src/test/scala/tests/FullTextSpec.scala\n\n### Keyword Searching\n\nAs we saw previously, the `fullText` field provides us with a concatenation of all fields configured to be `fullTextSearchable`.\nIn addition, if you create an instance of `KeywordIndexing` you can query against a no-duplicates index of keywords for\nthe `fullText` (although you can override defaults to apply keyword indexing to any field). All we have to do is create\nand instance referencing the `Lucene` instance and the name (used for storage purposes):\n\n```\nval keywordIndexing = KeywordIndexing(lucene, \"keywords\")\nval keywords = keywordIndexing.search(\"do*\")\nprintln(\"Keywords: ${keywords.results.map(_.word).mkString(\", \")}\")\n```\n\nThe above code would output:\n\n```\nKeywords: Doe\n```\n\nFor the complete example see: https://github.com/outr/lucene4s/blob/master/implementation/src/test/scala/tests/SimpleSpec.scala\n\n### Case Class Support\n\nlucene4s provides a powerful Macro-based system to generate two-way mappings between case classes and Lucene fields at\ncompile-time. This is accomplished through the use of `Searchable`.  The setup is pretty simple.\n\n#### Setup\n\nFirst we need to define a case class to model the data in the index:\n\n```scala\ncase class Person(id: Int, firstName: String, lastName: String, age: Int, address: String, city: String, state: String, zip: String)\n```\n\nAs you can see, this is a bare-bones case class with nothing special about it.\n\nNext we need to define a `Searchable` trait the defines the unique identification for update and delete:\n\n```scala\ntrait SearchablePerson extends Searchable[Person] {\n  // This is necessary for update and delete to reference the correct document.\n  override def idSearchTerms(person: Person): List[SearchTerm] = List(exact(id(person.id)))\n  \n  /*\n    Though at compile-time all fields will be generated from the params in `Person`, for code-completion we can define\n    an unimplemented method in order to properly reference the field. This will still compile without this definition,\n    but most IDEs will complain.\n   */\n  def id: Field[Int]\n}\n```\n\nAs the last part of our set up we simply need to generate it from our `Lucene` instance:\n\n```scala\nval people = lucene.create.searchable[SearchablePerson]\n```\n\n#### Inserting\n\nNow that we've configured everything inserting a person is trivial:\n\n```scala\npeople.insert(Person(1, \"John\", \"Doe\", 23, \"123 Somewhere Rd.\", \"Lalaland\", \"California\", \"12345\")).index()\n```\n\nNotice that we still have to call `index()` at the end for it to actually invoke. This allows us to do more advanced\ntasks like adding facets, adding non-Searchable fields, etc. before actually inserting.\n\n#### Updating\n\nNow lets try updating our `Person`:\n\n```scala\npeople.update(Person(1, \"John\", \"Doe\", 23, \"321 Nowhere St.\", \"Lalaland\", \"California\", \"12345\")).index()\n```\n\nAs you can see here, the signature is quite similar to `insert`. Internally this will utilize `idSearchTerms` as we\ndeclared previously to apply the update. In this case that means as long as we don't change the id (1) then calls to\nupdate will replace an existing record if one exists.\n\n#### Querying\n\nQuerying works very much the same as in the previous examples, except we get our `QueryBuilder` from our `people`\ninstance:\n\n```scala\nval paged = people.query().search()\npaged.entries.foreach { person =\u003e\n  println(s\"Person: $person\")\n}\n```\n\nNote that instead of calling `paged.results` we call `paged.entries` as it represents the conversion to `Person`. We can\nstill use `paged.results` if we want access to the `SearchResult` like before.\n\n#### Deleting\n\nDeleting is just as easy as inserting and updating:\n\n```scala\npeople.delete(Person(1, \"John\", \"Doe\", 23, \"321 Nowhere St.\", \"Lalaland\", \"California\", \"12345\"))\n```\n\n#### Additional Information\n\nAll `Searchable` implementations automatically define a `docType` field that is used to uniquely separate different\n`Searchable` instances so you don't have to worry about multiple different instances overlapping.\n\nFor more examples see https://github.com/outr/lucene4s/blob/master/implementation/src/test/scala/tests/SearchableSpec.scala\n\n### Geospatial Support\n\nOne of the great features of Lucene is geospatial querying and what Lucene wrapper would be complete without it?\n\n#### Creating a Spatial Field\n\nIn order to create a stored, queryable, filterable, and sortable latitude and longitude you need only create a\n`SpatialPoint` field:\n\n```scala\nval location: Field[SpatialPoint] = lucene.create.field[SpatialPoint](\"location\")\n```\n\n#### Sorting Nearest a Point\n\nMost of the time it's most useful to take an existing latitude and longitude and sort your results returning the\nnearest documents to that location:\n\n```scala\nval paged = lucene.query().sort(Sort.nearest(location, SpatialPoint(40.7142, -74.0119))).search()\n```\n\n#### Filtering by Distance\n\nIf you want to filter your results to only include entries within a certain range of a location:\n\n```scala\nval newYorkCity = SpatialPoint(40.7142, -74.0119)\nval paged = lucene\n  .query()\n  .sort(Sort.nearest(location, newYorkCity))\n  .filter(spatialDistance(location, newYorkCity, 50.miles))\n  .search()\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foutr%2Flucene4s","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foutr%2Flucene4s","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foutr%2Flucene4s/lists"}