ตัวแยกวิเคราะห์ซีเรียลไลเซอร์และเครื่องมือค้นหาที่มีน้ำหนักเบาและมีประสิทธิภาพเหมือน Lucene
เดิมทีสร้าง Liqe เพื่อเปิดใช้งานการกรองบันทึก Roarr ผ่าน cli ตั้งแต่นั้นมา ฉันได้ขัดเกลาโครงการนี้ให้เป็นงานอดิเรก/แบบฝึกหัดทางปัญญา ฉันเห็นว่ามีการใช้ CLI และเว็บแอปพลิเคชันต่างๆ ที่ต้องมีการค้นหาขั้นสูง ตามความรู้ของฉัน ปัจจุบันเป็นตัวแยกวิเคราะห์ไวยากรณ์และซีเรียลไลเซอร์ที่คล้ายกับ Lucene ที่สมบูรณ์แบบที่สุดใน JavaScript รวมถึงเครื่องมือค้นหาในหน่วยความจำที่เข้ากันได้
กรณีการใช้งาน Liqe ได้แก่:
โปรดทราบว่า Liqe AST ถือเป็น API สาธารณะ กล่าวคือ สามารถใช้กลไกการค้นหาของตนเองที่ใช้ภาษาคิวรี Liqe (LQL) ได้
import {
filter ,
highlight ,
parse ,
test ,
} from 'liqe' ;
const persons = [
{
height : 180 ,
name : 'John Morton' ,
} ,
{
height : 175 ,
name : 'David Barker' ,
} ,
{
height : 170 ,
name : 'Thomas Castro' ,
} ,
] ;
กรองคอลเลกชัน:
filter ( parse ( 'height:>170' ) , persons ) ;
// [
// {
// height: 180,
// name: 'John Morton',
// },
// {
// height: 175,
// name: 'David Barker',
// },
// ]
ทดสอบวัตถุเดียว:
test ( parse ( 'name:John' ) , persons [ 0 ] ) ;
// true
test ( parse ( 'name:David' ) , persons [ 0 ] ) ;
// false
เน้นฟิลด์และสตริงย่อยที่ตรงกัน:
highlight ( parse ( 'name:john' ) , persons [ 0 ] ) ;
// [
// {
// path: 'name',
// query: /(John)/,
// }
// ]
highlight ( parse ( 'height:180' ) , persons [ 0 ] ) ;
// [
// {
// path: 'height',
// }
// ]
Liqe ใช้ Liqe Query Language (LQL) ซึ่งได้รับแรงบันดาลใจอย่างมากจาก Lucene แต่ขยายออกไปในรูปแบบต่างๆ ที่ให้ประสบการณ์การค้นหาที่ทรงพลังยิ่งขึ้น
# search for "foo" term anywhere in the document (case insensitive)
foo
# search for "foo" term anywhere in the document (case sensitive)
'foo'
"foo"
# search for "foo" term in `name` field
name :foo
# search for "foo" term in `full name` field
'full name' : foo
"full name" : foo
# search for "foo" term in `first` field, member of `name`, i.e.
# matches {name: {first: 'foo'}}
name . first :foo
# search using regex
name :/foo /
name :/ foo / o
# search using wildcard
name :foo * bar
name :foo? bar
# boolean search
member :true
member :false
# null search
member :null
# search for age =, >, >=, <, <=
height : = 100
height :>100
height :>=100
height :<100
height :<=100
# search for height in range (inclusive, exclusive)
height : [ 100 TO 200 ]
height : { 100 TO 200 }
# boolean operators
name :foo AND height : = 100
name :foo OR name : bar
# unary operators
NOT foo
- foo
NOT foo : bar
- foo :bar
name :foo AND NOT ( bio :bar OR bio : baz )
# implicit AND boolean operator
name :foo height : = 100
# grouping
name :foo AND ( bio :bar OR bio : baz )
ค้นหาคำว่า "foo" ในฟิลด์ใดก็ได้ (ไม่คำนึงถึงตัวพิมพ์เล็กและใหญ่)
foo
ค้นหาคำว่า "foo" ในช่อง name
name :foo
ค้นหาค่าฟิลด์ name
ที่ตรงกับ /foo/i
regex
name :/foo / i
ค้นหาค่าฟิลด์ name
ที่ตรงกับรูปแบบสัญลักษณ์ตัวแทน f*o
name :f * o
ค้นหาค่าฟิลด์ name
ที่ตรงกับรูปแบบสัญลักษณ์ f?o
name :f? o
ค้นหาวลี "foo bar" ในฟิลด์ name
(คำนึงถึงตัวพิมพ์เล็กและตัวพิมพ์ใหญ่)
name :"foo bar"
ค้นหาค่าเท่ากับ 100 ในช่อง height
height : = 100
ค้นหาค่าที่มากกว่า 100 ในช่อง height
height :>100
ค้นหาค่าที่มากกว่าหรือเท่ากับ 100 ในช่อง height
height :>=100
ค้นหาค่าที่มากกว่าหรือเท่ากับ 100 และต่ำกว่าหรือเท่ากับ 200 ในช่อง height
height : [ 100 TO 200 ]
ค้นหาค่าที่มากกว่า 100 และต่ำกว่า 200 ในช่อง height
height : { 100 TO 200 }
ค้นหาคำที่ขึ้นต้นด้วย "foo" ในช่อง name
name :foo *
ค้นหาคำที่ขึ้นต้นด้วย "foo" และลงท้ายด้วย "bar" ในช่อง name
name :foo * bar
ค้นหาคำใดๆ ที่ขึ้นต้นด้วย "foo" ในช่อง name
ตามด้วยอักขระที่กำหนดเองเพียงตัวเดียว
name :foo?
ค้นหาคำใดๆ ที่ขึ้นต้นด้วย "foo" ตามด้วยอักขระที่กำหนดเองเพียงตัวเดียว และลงท้ายด้วย "bar" ในช่อง name
ทันที
name :foo? bar
ค้นหาวลี "foo bar" ในช่อง name
และวลี "quick fox" ในช่อง bio
name :"foo bar" AND bio : "quick fox"
ค้นหาวลี "foo bar" ในช่อง name
และวลี "quick fox" ในช่อง bio
หรือคำว่า "fox" ในช่อง name
( name :"foo bar" AND bio : "quick fox" ) OR name : fox
Serializer อนุญาตให้แปลงโทเค็น Liqe กลับไปเป็นคำค้นหาดั้งเดิม
import {
parse ,
serialize ,
} from 'liqe' ;
const tokens = parse ( 'foo:bar' ) ;
// {
// expression: {
// location: {
// start: 4,
// },
// quoted: false,
// type: 'LiteralExpression',
// value: 'bar',
// },
// field: {
// location: {
// start: 0,
// },
// name: 'foo',
// path: ['foo'],
// quoted: false,
// type: 'Field',
// },
// location: {
// start: 0,
// },
// operator: {
// location: {
// start: 3,
// },
// operator: ':',
// type: 'ComparisonOperator',
// },
// type: 'Tag',
// }
serialize ( tokens ) ;
// 'foo:bar'
import {
type BooleanOperatorToken ,
type ComparisonOperatorToken ,
type EmptyExpression ,
type FieldToken ,
type ImplicitBooleanOperatorToken ,
type ImplicitFieldToken ,
type LiteralExpressionToken ,
type LogicalExpressionToken ,
type RangeExpressionToken ,
type RegexExpressionToken ,
type TagToken ,
type UnaryOperatorToken ,
} from 'liqe' ;
มีโทเค็น AST 11 โทเค็นที่อธิบายแบบสอบถาม Liqe ที่แยกวิเคราะห์
หากคุณกำลังสร้างซีเรียลไลเซอร์ คุณต้องใช้งานทั้งหมดเพื่อให้ครอบคลุมอินพุตคิวรีที่เป็นไปได้ทั้งหมด อ้างถึงซีเรียลไลเซอร์ในตัวสำหรับตัวอย่าง
import {
isSafeUnquotedExpression ,
} from 'liqe' ;
/**
* Determines if an expression requires quotes.
* Use this if you need to programmatically manipulate the AST
* before using a serializer to convert the query back to text.
*/
isSafeUnquotedExpression ( expression : string ) : boolean ;
ไม่รองรับความสามารถของ Lucene ต่อไปนี้:
ในกรณีที่มีข้อผิดพลาดทางไวยากรณ์ Liqe จะส่ง SyntaxError
import {
parse ,
SyntaxError ,
} from 'liqe' ;
try {
parse ( 'foo bar' ) ;
} catch ( error ) {
if ( error instanceof SyntaxError ) {
console . error ( {
// Syntax error at line 1 column 5
message : error . message ,
// 4
offset : error . offset ,
// 1
offset : error . line ,
// 5
offset : error . column ,
} ) ;
} else {
throw error ;
}
}
พิจารณาใช้แพ็คเกจ highlight-words
เพื่อเน้นการจับคู่ Liqe
หากคุณกำลังจะแก้ไข parser ให้ใช้ npm run watch
เพื่อรันคอมไพเลอร์ในโหมด watch
ก่อนทำการเปลี่ยนแปลงใดๆ ให้บันทึกการวัดประสิทธิภาพปัจจุบันบนเครื่องของคุณโดยใช้ npm run benchmark
เรียกใช้การวัดประสิทธิภาพอีกครั้งหลังจากทำการเปลี่ยนแปลงใดๆ ก่อนดำเนินการเปลี่ยนแปลง ตรวจสอบให้แน่ใจว่าประสิทธิภาพไม่ได้รับผลกระทบในทางลบ