forked from anykeyh/clear
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Yacine Petitprez
committed
Jul 2, 2018
1 parent
369e832
commit 0c8897d
Showing
7 changed files
with
219 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
35 changes: 35 additions & 0 deletions
35
manual/extensions/full_text_searchable/FullTextSearchable.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
Full text search plugin offers full integration with `tsvector` capabilities of | ||
Postgresql. | ||
|
||
It allows you to query models through the text content of one or multiple fields. | ||
|
||
### The blog example | ||
|
||
Let's assume we have a blog and want to implement full text search over title and content: | ||
|
||
```crystal | ||
create_table "posts" do |t| | ||
t.string "title", nullable: false | ||
t.string "content", nullable: false | ||
t.full_text_searchable on: [{"title", 'A'}, {"content", 'C'}] | ||
end | ||
``` | ||
|
||
This migration will create a 3rd column named `full_text_vector` of type `tsvector`, | ||
a gin index, a trigger and a function to update automatically this column. | ||
|
||
Over the `on` keyword, '{"title", 'A'}' means it allows search of the content of "title", with level of priority (weight) "A", which tell postgres than title content is more meaningful than the article content itself. | ||
|
||
Now, let's build some models: | ||
|
||
```crystal | ||
Post.create!({title: "About poney", content: "Poney are cool"}) | ||
Post.create!({title: "About dog and cat", content: "Cat and dog are cool. But not as much as poney"}) | ||
Post.create!({title: "You won't believe: She raises her poney like as star!", content: "She's col because poney are cool"}) | ||
``` | ||
|
||
Search is now easily done | ||
``` | ||
Post.query.search("poney") # Return all the articles ! | ||
``` |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
class Clear::TSVector | ||
struct Lexem | ||
record Position, weight : Char, position : UInt16 | ||
|
||
getter value : String = "" | ||
getter positions : Array(Position) = [] of Position | ||
|
||
WEIGHTS = ['A', 'B', 'C', 'D'] | ||
|
||
def initialize(io) | ||
chars = [] of UInt8 | ||
|
||
while ((c = io.read_byte.not_nil!) != 0) | ||
chars << c | ||
end | ||
|
||
@value = String.new(chars.to_unsafe, chars.size) | ||
|
||
pos_size : UInt16 = 0_u16 | ||
|
||
pos_size |= io.read_byte.not_nil! << 8 | ||
pos_size |= io.read_byte.not_nil! << 0 | ||
|
||
pos_size.times do | ||
pos_off_and_weight : UInt16 = 0_u16 | ||
|
||
pos_off_and_weight |= io.read_byte.not_nil! << 8 | ||
pos_off_and_weight |= io.read_byte.not_nil! << 0 | ||
|
||
w = WEIGHTS[(pos_off_and_weight & 0xC000) >> 14] | ||
|
||
@positions << Position.new(w, pos_off_and_weight & (~0xC000)) | ||
end | ||
end | ||
end | ||
|
||
getter lexems : Hash(String, Lexem) = {} of String => Lexem | ||
|
||
def [](key : String) | ||
lexems[key] | ||
end | ||
|
||
def []?(key : String) | ||
lexems[key]? | ||
end | ||
|
||
def to_db | ||
@lexems.values.map do |v| | ||
{ | ||
Clear::Expression[v.value], | ||
v.positions.map { |p| {p.position, p.weight}.join }.join(","), | ||
}.join(":") | ||
end.join(" ") | ||
end | ||
|
||
def initialize(io) | ||
size : UInt32 = 0 | ||
|
||
size |= io.read_byte.not_nil! << 24 | ||
size |= io.read_byte.not_nil! << 16 | ||
size |= io.read_byte.not_nil! << 8 | ||
size |= io.read_byte.not_nil! << 0 | ||
|
||
size.times.each do |x| | ||
l = Lexem.new(io) | ||
@lexems[l.value] = l | ||
end | ||
end | ||
|
||
def self.decode(x : Slice(UInt8)) | ||
io = IO::Memory.new(x, writeable: false) | ||
Clear::TSVector.new(io) | ||
end | ||
|
||
module Converter | ||
def self.to_column(x) : Clear::TSVector? | ||
case x | ||
when String | ||
data = x | ||
s = Slice(UInt8).new(data.to_unsafe, data.size) | ||
return Clear::TSVector.decode(s) | ||
when Slice(UInt8) | ||
return Clear::TSVector.decode(x) | ||
when Clear::TSVector | ||
return x | ||
when Nil | ||
return nil | ||
else | ||
raise "Cannot convert #{x.class} to TSVector" | ||
end | ||
end | ||
|
||
def self.to_db(x : TSVector?) | ||
if (x) | ||
x.to_db | ||
else | ||
nil | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters