-
Notifications
You must be signed in to change notification settings - Fork 340
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[9.x] Add a DatabaseEngine #564
Conversation
We will want a way to also search on normal columns using LIKE or just normal value check like against IDs, etc. Inspiration for this can be taken from Nova's database search functionality which builds the |
How to decide which one to use? Fulltext search and checking against IDs can be index supported, wildcard search with a leading wildcard can not use an index (except on PostgreSQL with pg_trgm). But wildcard search with a trailing wildcard can use an index, but again only if it is indexed and only for an index which will support the operation (hash indexes won't work). And even for fulltext search scout has to know which columns are fulltext indexed or the query will be stopped with an error on missing index (MySQL) or will be slow (PostgreSQL). |
@taylorotwell normal value checks are already supported: $invoices = Invoice::search('Forge')->where('id', 5)->get(); For use Laravel\Scout\Builder;
$invoices = Invoice::search('Forge', function ($query, Builder $builder string $search) {
$query->where('title', 'LIKE', $search);
})->get(); That'll invoke the fulltext search and an additional We could also add a new $invoices = Invoice::search('Forge')->whereLike('title', 'Forge')->get(); But that might be a bit too much and confusion for the other engines? @taylorotwell I actually want to know more why you want to support LIKE and additional column clauses. It seems to me we need to focus this all towards fulltext indexes? Like @tpetry says, I don't think it's ideal to mix things up with LIKE, etc. |
@driesvints it's extremely common to search by primary ID in business applications (for example, order ID) in addition to fields that may be full text indexed like a shipping address or order notes field using a single search input field. So, for example, if I type "1" in the search box I may want to find the user with an ID of 1, while I can also type "Arkansas" in the search box to find orders with "Arkansas" in the full-text indexed notes. Without that functionality I frankly would not find a database driver for Scout useful at all. The type of search functionality I'm proposing is exactly the kind of search functionality offered by the search field in Laravel Nova, which I think I noted that code may provide some good inspiration here as it works really well across multiple database drivers. I will look into this PR more tomorrow and will likely work on the implementation if I can. |
How will you know which field(s) to search? Sounds more like the ID is part of a separate filter than the search box?
I'll investigate that. |
I like that approach but had no idea until now how to do it. A possible solution for the database driver could be a specification for every column. I like the approach of pmatseykanets/laravel-scout-postgres. Additionally to the normal scout configuration, another array is defined with options for the driver. The search method would be defined for every column ( Either way a configuration method on the model needs to be supported to pass the |
Does that mean that we add "or" clauses for every mapped column? |
That was my first idea. But it does not work as defining multiple columns for fulltext searching would try to do a fulltext search on each by it's own and then Maybe something like this (i don't like the structure, just sharing the idea): public function searchableOptions()
{
return [
'columns' => [
'like_search' => ['id', 'email'], // these can be "or"ed
'fulltext_search' => [ // each group needs to use one whereFullText and then can be "or"ed
['body', 'title],
['subject', 'description],
],
],
];
} |
Hey all, I've pushed my work for today which is a pretty big rewrite of this. It now behaves more like Laravel Nova search with the added ability for real full-text searching with the new I built a small search interface for myself to test it with: OverviewSo, given the following The following naive, LIKE based query would be executed by default, allowing very basic searching across all columns: This is exactly like Nova. Note - this portion of the query is grouped within parenthesis, again, like Nova, for proper logical grouping if the developer adds additional If you try to search by an integer and the primary key type is an integer for the model, it will attempt an exact search against that value instead of a LIKE - again, just like Nova. Full Text SupportYou may easily specify which columns actually have full text indexes using Attributes: This now generates a query like so: LIKE Without Wildcard PrefixSome columns you may want to be searched using Other ImprovementsI have made quite a few other improvements. Most notably, the original implementation of this driver executed 2 database queries... one to perform the search and then an extra, extraneous query on all the matching models by ID using a I've also updated the pagination logic to handle the LIMIT and OFFSET at the database level instead of potentially returning thousands of models to Laravel and doing it via the collections. CaveatsThe Scout Therefore, when using the database engine users would be encouraged / required to add the proper where constraints to the query when searching: Post::search('foo')->where('published', true)->get(); Otherwise, we would be required to retrieve every matching model into memory (even when paginating) and manually invoke |
Does fulltext approach , instead MeiliSeach or ElasticSearch, implies in too much heavy load on database? Locks ? Performance? Does anybody has any concerns about that ? I really would love to get rid off that extra piece of infrastructure (Melisearch/Elastic) |
+1 |
@taylorotwell this looks amazing, thanks! I do believe the Most likely we'll need to provide a way to map those options separate per column. /cc @tpetry ☝️ |
} | ||
|
||
$query->orWhere( | ||
$builder->model->qualifyColumn($column), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$builder->model->qualifyColumn($column), | |
$query->qualifyColumn($column), |
foreach ($columns as $column) { | ||
if (in_array($column, $fullTextColumns)) { | ||
$query->orWhereFullText( | ||
$builder->model->qualifyColumn($column), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$builder->model->qualifyColumn($column), | |
$query->qualifyColumn($column), |
Available since 5.5 laravel/framework#22577
I have no idea on the performance. Depends on the size of your database I'm sure. This driver is intended for usage on smaller data sets and workloads where such performance isn't a concern. |
It's highly dependent on your database, your data model and your queries. In the past MySQL was pretty bad when you did a fulltext search with a condition to filter rows because MySQL was not able to use multiple indexes for a single query. But that has changed with 8.0, whether it will use multiple indexes or not is still depending on a lot of things and some magic of MySQL's query planner. And it's still not choosing multiple indexes today very often, but it's getting better and better. I only have recent experience with PostgreSQL's fulltext search which is working really nice with a few million entries. And as PostgreSQL will use multiple indexes since 8.1 (2005) the query planner is really good at it and you can blindly throw queries at it and it will execute the fulltext queries very fast. Combined with tight control of word stemming, relevances for columns and ranking for sorting it's a good simpler alternative to Melisearch/Elastic for low/medium workloads. But sure, a single database server can't beat a 6 node Elastic cluster. As said, there is no general statement to performance, it depends on a lot of things. It's best you try your workload and if it's working you can be happy to remove Melisearch/Elastic from your stack. -- I just wanted to add that everyone is concerned about "performance". Everyone is concerned about slow databases queries. Databases can do a lot more things efficiently than most people believe. Instead of researching performance problems of solution x just implement it and test it. Nobody is able to predict special behaviour accurately (except if you do stupid things). And everything is always completely dependent on your data model, data size and querying behaviour. Something working perfectly for me may work horrible for you because these 3 properties are different for you - but most often only the first and last one make a difference. |
✔️ |
This PR adds a new database engine to Scout that's powered by the upcoming
whereFulltext
addition to Laravel's query builder. Using this, we'll be able to offer natural language search in Scout onfulltext
indexes for Scout.I managed to quickly set this up thanks to the existing Collection engine. We'll probably need to abstract those so functionality is shared.
The way this works is that you define the columns of your index through the
toSearchableArray
method:Then use it as usual:
where
statements to limit results are also supported:Later on when
whereFulltext
gets supported by more database engines in Laravel, the upgrade will be seamless.Todo
fulltext
indexes.whereFulltext
method. Maybe through thescoutMetadata
method?