In my web project on Playframework-e, one day a search was required. I immediately dismissed the idea of searching the database through like, because I wanted rankings and other “smart” search buns, and there was no time or desire to reinvent my own bike.
Since the project is in Java - it was very tempting to use Lucene for this.
In Google, I immediately found a wonderful module for Playframework called
Search , the
Elastic Search module was also found, which also uses Lucene, but it requires the installation of a separate server, and therefore was rejected. I liked the Search module because of its simplicity - all the bells and whistles are encapsulated in it, so it's very easy to use.
With the installation of the module, as always in Play-e, there were no problems, the
play install search team worked with a bang and pumped out the module from the repository.
Having added
module.search = $ {play.path} /modules/search-2.0 to
application.conf, I could already use it in the application.
Following a brief tutorial, I added an
Entry to the entity, which actually needed to be searched, the
@Indexed annotation, and the description field the
@Field annotation.
Writing the following code in the controller:
public static void search(String phrase, int page) { int pageSize = PAGE_SIZE; Query query = Search.search("description:" + phrase, Entry.class); List<Entry> entries = query.page(page*pageSize, pageSize).fetch(); long totalCount = query.count(); render(entries, totalCount, page, pageSize, phrase); }
I was ready to do the first tests and build up the functionality, but then the problems started ...
The search did not work, that is, the
count () method returned
0 , and the list of
entries was empty. I tried to search in both Russian and English, and called
Search.rebuildAllIndexes () , and I tried many other things, but the result was unchanged.
Fortunately, the module in the play-e is downloaded along with the source code and could be downloaded. A brief debug showed that the
description field is not put in the index. I climbed a little deeper and saw that in the search for the
@Field annotation, the
object field uses the
object.getClass () method
. getFields () , but stop, this method returns only public fields, and in my essence fields, as it should be, have protected access, and the module author should have used the
getDeclaredFields () method.
Here I will make some digression: I absolutely did not want to rebuild the module and change the code in it, at least because I would lose the opportunity to make a play install search on a “combat” server, but I would have to put a reassembled module in my hands. It was not a quick idea to write a bug report or suggest a patch, and the functionality was needed right now.In general, it was decided to make the
description field public, and write todo, until the bug is fixed in the module. And, lo and behold, the search has earned!
The next problem, which I already guessed after reading the tutorial, was that the module had a “killer feature” - automatic updating of the index (and adding new records) for CRUD operations with the entity — I did not need it. The fact is that the records that are added to the system first go through pre-moderation and I absolutely don’t need the “unmoderated” records to go into the search.
I want to call
Search.index (entry) myself after moderation. I really hoped that I would find in the source code a check for some line in the config file: do automatic index updates or not, but I came across
this in the SearchPlugin code:
@Override public void onEvent(String message, Object context) { if (!message.startsWith("JPASupport")) return; if (message.equals("JPASupport.objectPersisted") || message.equals("JPASupport.objectUpdated")) { Search.index (context); } else if (message.equals("JPASupport.objectDeleted")) { Search.unIndex(context); } }
It was impossible to disconnect it, and it seems to be time to think about the need to send a patch and kick in order to quickly release a version with a fix, but a brilliant idea was born here: after all, in fact I’ll still be a module or just a set of libraries which I put in the lib folder.
Digging in the code showed that when you start the application you need to perform:
Search.init(); FileExtractor.init();
And when you stop:
try { Search.shutdown(); } catch (Exception e) { throw new UnexpectedException (e); }
This is easily done using the
@ OnApplicationStart / Stop job annotations.
The next step was to “wean” play, thinking that this is a plugin. Playframework finds the modules in the classpath with the help of the play.plugins
file , with the usual archiver itself, this file was removed from the jar file, and everything started spinning.
I hope my experience will be useful and will help save time for people who use Playframework in their work.
PS: Since I had to put the module in the lib folder, I was at the same time rebuilding it by correcting a bug with public fields. :)