Project

neighbor

0.04
The project is in a healthy, maintained state
Nearest neighbor search for Rails and Postgres
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
 Project Readme

Neighbor

Nearest neighbor search for Rails and Postgres

Build Status

Installation

Add this line to your application’s Gemfile:

gem 'neighbor'

And run:

bundle install
rails generate neighbor:install
rails db:migrate

This enables the cube extension in Postgres

Getting Started

Create a migration

class AddNeighborVectorToItems < ActiveRecord::Migration[6.1]
  def change
    add_column :items, :neighbor_vector, :cube
  end
end

Add to your model

class Item < ApplicationRecord
  has_neighbors dimensions: 3
end

Update the vectors

item.update(neighbor_vector: [1.0, 1.2, 0.5])

With cosine distance (the default), vectors are normalized before being stored

Get the nearest neighbors to a record

item.nearest_neighbors.first(5)

Get the nearest neighbors to a vector

Item.nearest_neighbors([0.9, 1.3, 1.1]).first(5)

Distance

Specify the distance metric

class Item < ApplicationRecord
  has_neighbors dimensions: 3, distance: "euclidean"
end

Supported values are:

  • cosine (default)
  • euclidean
  • taxicab
  • chebyshev

For inner product, see this example

Records returned from nearest_neighbors will have a neighbor_distance attribute

nearest_item = item.nearest_neighbors.first
nearest_item.neighbor_distance

Dimensions

By default, Postgres limits the cube data type to 100 dimensions. See the Postgres docs for how to increase this.

Example

You can use Neighbor for online item-based recommendations with Disco. We’ll use MovieLens data for this example.

Generate a model

rails generate model Movie name:string neighbor_vector:cube
rails db:migrate

And add has_neighbors

class Movie < ApplicationRecord
  has_neighbors dimensions: 20
end

Fit the recommender

data = Disco.load_movielens
recommender = Disco::Recommender.new(factors: 20)
recommender.fit(data)

Use item factors for the neighbor vector

recommender.item_ids.each do |item_id|
  Movie.create!(name: item_id, neighbor_vector: recommender.item_factors(item_id))
end

And get similar movies

movie = Movie.find_by(name: "Star Wars (1977)")
movie.nearest_neighbors.first(5).map(&:name)

Complete code

History

View the changelog

Contributing

Everyone is encouraged to help improve this project. Here are a few ways you can help:

To get started with development:

git clone https://github.com/ankane/neighbor.git
cd neighbor
bundle install
bundle exec rake test