Futese
1.0.0
“未來”代表fu ll te x s se拱門。
這是一個簡單的內存持久性全文搜索引擎,不到1000行C#代碼:
ThreadSafeIndex<T> )和使用ContrentDictionary( ConcurrentIndex<T> )的並發版本的線程安全率。 ConcurrentIndex<T>使用更多的內存。 // create an index with string keys
var index = new Index<string>();
// add keys and phrases
index.Add("a", "This is a simple phrase");
index.Add("b", "And this one is another phrase a bit longer");
index.Add("c", "The last phrase (this one) contains french (with diacritics) like 'réveillez-vous à l'heure!'");
// search
SimpleTest(index);
// persist to a file
var fileName = "test.fts";
index.Save(fileName);
// load from a file
var newIndex = new Index<string>(); // or new ThreadSafe<string>() for example
newIndex.Load(fileName);
// search again
SimpleTest(newIndex);
newIndex.KeysCount.Should().Be(index.KeysCount);
static void SimpleTest(Index<string> index)
{
string[] result;
result = index.Search("this").Distinct().ToArray();
result.Should().BeEquivalentTo(["a", "b", "c"]);
result = index.Search("this is").Distinct().ToArray();
result.Should().BeEquivalentTo(["a", "b"]);
result = index.Search("simple | with").Distinct().ToArray();
result.Should().BeEquivalentTo(["a", "c"]);
result = index.Search("that").Distinct().ToArray();
result.Should().BeEquivalentTo([]);
result = index.Search("the").Distinct().ToArray();
result.Should().BeEquivalentTo(["c"]);
result = index.Search("rev").Distinct().ToArray();
result.Should().BeEquivalentTo(["c"]);
result = index.Search("-one").Distinct().ToArray();
result.Should().BeEquivalentTo(["a"]);
result = index.Search("-this | last").Distinct().ToArray();
result.Should().BeEquivalentTo([]);
}
您也可以使用複雜的密鑰,例如,該Customer類可以用作密鑰,並且其數據也將被持續使用:
// a key must be IParsable and should generally implement IEquatable<T>
private sealed class Customer(int id, string firstName, string lastName, int age) :
IParsable<Customer>, IEquatable<Customer>
{
public int Id => id;
public string FirstName => firstName;
public string LastName => lastName;
public int Age => age;
// used when saved to a stream.
// by default, the index will use object.ToString() to persist the key,
// but you can also implement IStringable.ToString()
public override string ToString() => id + "t" + firstName + "t" + lastName + "t" + age;
// use id as the real key
public override int GetHashCode() => id.GetHashCode();
public override bool Equals(object? obj) => Equals(obj as Customer);
public bool Equals(Customer? other) => other != null && other.Id == id;
// used when loading from a stream
public static Customer Parse(string s, IFormatProvider? provider)
{
var split = s.Split('t');
return new Customer(int.Parse(split[0]), split[1], split[2], int.Parse(split[3]));
}
// not called by futese which expects Parse to succeed
public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, [MaybeNullWhen(false)] out Customer result) => throw new NotImplementedException();
}
這就是您的使用方式:
var index = new Index<Customer>();
index.Add(new(0, "alice", "hunting-bobby-crown", 25));
index.Add(new(1, "bob", "albert-down", 32));
index.Add(new(2, "carl", "ctrl-alt", 15));
// search
TestWithObjects(index);
// persist to a file
var fileName = "customer.fts";
index.Save(fileName);
// load from a file
var newIndex = new Index<Customer>();
newIndex.Load(fileName);
// search again
TestWithObjects(newIndex);
static void TestWithObjects(Index<Customer> index)
{
Customer[] result;
result = index.Search("al").Distinct().ToArray();
result.Should().OnlyContain(c => c.FirstName == "alice" || c.FirstName == "bob" || c.FirstName == "carl");
result = index.Search("b").Distinct().ToArray();
result.Should().OnlyContain(c => c.FirstName == "alice" || c.FirstName == "bob");
result = index.Search("a -c").Distinct().ToArray();
result.Should().OnlyContain(c => c.FirstName == "bob");
result = index.Search("a c").Distinct().ToArray();
result.Should().OnlyContain(c => c.FirstName == "alice" || c.FirstName == "carl");
result = index.Search("a d").Distinct().ToArray();
result.Should().OnlyContain(c => c.FirstName == "bob");
result = index.Search("hunting a").Distinct().ToArray();
result.Should().OnlyContain(c => c.FirstName == "alice");
}