 Hi, my name is Christoph. I'm an engineer at NexCloud and in this workshop I will show you how you can integrate a NexCloud app into the NexCloud unified search For the search demo, I would like to write an app that uses the Wikipedia public API to search for Wikipedia articles. I chose this API because it doesn't require an API key, so it's very easy to set up for everyone. We can see here the preview page of the of the API. So for our search term NexCloud, we get back a list of entries with like a title, page ID, a snippet and so on. And also has some information on the page nation like the offset. Before we look into the code of the NexCloud search, we should discuss page nation. Understanding this concept makes it easier to work with the unified search API. The concept of page nation is about splitting a set of elements into chunks. So it's usually easier to process them. With our Wikipedia integration that means that every time we ask for search results, it will only give us a limited number of elements. And then we have to do another request and another one. So eventually we can have the full set. Now the two ways to do page nation is to either look at the indexes like the position of the elements or base it on an attribute of each of the rows. Let's start with the index based page nation, also known maybe as offset and limit. So when we fetch our first page here, we will get, let's say, five elements and that is index zero to index four. So for our next page, we will request anything that is after index four. So we will get back five to index nine. For the next page, we will get index 10 to index 14. So far so good, but there is a catch. What if the data that we want to query changes like entries vanish and other ones are added? So we fetch our first page again and get back the five elements as we did before. But before we can fetch the second page, one of the elements of the first page is deleted from this API. Which means the indexes shift. So as we request anything after index four, there is one element, the new index four, which we'll never get. I just call it a phantom here. Also, the next page will be shifted by one index because of the deleted entry. Let's look at the alternative of this index based page nation, which uses the sort attribute as a cursor. The sort attribute or property or column is that one field of the result entries that is used for sorting. So the premise of page nation is in general that the entries are sorted by an attribute because you need consistency from one page to the next one. Here in this example, I assigned times to the individual rows. So you see the first page will be from 1412 to 1423. The next one will start at 2024. We fetch the first page pretty much like we did with the alternative approach. But for the second page, we don't specify an index at which the page should start. But we say that it should be anything after 1423. So it's not based on the position of the last element of the previous page, but it's about data. For the third page, the same applies, we just say give us anything that comes after 1449 and we get the results that are 4051 and following. If we now return to the problem that I discussed before of where data of the results changes, we again delete one of the elements of the first page. So let's assume we already fetched the first page, then that element is dropped. And as we fetch the second page, we say give us anything after 1423. And now here you can see that the page actually stays at the same elements. It is not influenced by the removal of this one entry. So it's more stable. The same happens if a new element gets inserted, like at the position of the first page, the second page would be the same. And that is why if you have the option to choose between offset and the cursor, you should always opt for the cursor. Okay, so let's get started with the coding. Let me show you the app skeleton that I prepared for this demonstration. So we have our metadata in app info info XML, which just has the app ID, which is wikipedia underscore search. I started with 010 version, namespace will be wikipedia search. And I will make it compatible with the just announced and release next cloud 20, as well as the next cloud server master branch, which is which is already tagged as 21. Okay, so the other thing that is there already is the search service, which is a simple class with one important message, the search. And it takes a search term, which is a string and an optional offset. And turns that into a wikipedia search result. This wikipedia search result is a little helper class to transport the data like it's just an array of articles and the optional offset. So it's easier to return to values from this one method. We don't have to look into too much of the details, but basically it uses the wikipedia API that I showed before. It adds our search term URL encoded and then parses the JSON that is returned and transforms that into this wikipedia search result and the array of wikipedia articles. And here we see we can extract the title from the from each of the results. And we can build a URL based on the page ID. This is where we get the offset. So if it's not set, then we just have null. But otherwise we use whatever wikipedia tells us to use for the next for the next paginated request. Okay, assuming that this code will just work, we can start our search provider. The search provider is the class, it's like a glue code that connects the next cloud unified search with your app or it tells next cloud how you use unified search for your app. The class has to implement the provider of ocp search. So let's take a look here. It's an interface with four methods. There is the get ID, which I guess we will just use the application ID. There's a name which is shown for each of the groups that you see in the user interface return for the search results. There is an order which determines the positioning of a result set. Like you have results combined from multiple sources and some are more important like say from next cloud talk because it's a read time application. And wikipedia entries are less important. So we will use a high number here. And then the core method is search, which here we get the user. So we have some context that is not used for our app, but for others it's relevant. And we get the search query, which will tell us the search term and the order if that is specified. Limit like how many elements we should fetch a cursor. And then also if we need that for our context it tells us which route and which parameters are used right now. That's not relevant for our app though. Okay. And what we have to return is a search result. Let's look at that class as well. It is a serializable class. So that will be returned by the API of next cloud. And to construct it you can use to construct its private. But there are two factory methods. There is complete and there is paginated. It's possible to return the full set of results immediately without pagination. We use it for example for the unified search integration of searching settings in next cloud. That list will never be larger than let's say 20 entries. So pagination is a bit too much and doesn't add any benefits. So we can return the full set immediately. This is also helpful if you want to say there are no results at all. Then you return a complete result with an empty array. Paginated is what we will use. So here you have the name as discussed before. The entries which are the search result entry objects and the cursor. The cursor is used for the next call of the search function of the project. Function of the provider. And this can be a int or a string. Okay now let's go and create our own search provider that implements this interface. I'm creating a new namespace and here we will place the search provider. Let's call it Wikipedia search provider. The namespace was Wikipedia search without the lip. Now it chooses the wrong one. This is the one we want. Boom. We don't need a lesson set right now. But we will make it strict. So as I said we will implement the provider from search. We'll have the pgp store generate the steps. Okay so here I said we will use the application ID. So let's go ahead and also create an application class because we will need that later. That is always found in the app info namespace. Yeah we pgp store uses the wrong namespaces again. I don't know why. So we can drop the lesson set again. The application class has to extend the app from the app framework. And we want to implement the ipode strap. The new mechanism of next slide 20. More on that later. And here let's add a public const app ID and that will be Wikipedia search. Okay cool now we have that and can use it here. So the ID of the search provider is important to prevent conflicts. But if we like here use our app ID it's very unlikely that another search provider would use Wikipedia underscore search. For the name we want to use something like Wikipedia articles. But because that's use of interface string we should probably also translate it. So let's have the translation service in check that we added as a prop done. Now we can hear it here articles. Okay for the order as I said the Wikipedia search results are less important. So we return something high let's say 80. Now we can get to our actual implementation of the search. Method and for that we will need the search service that I showed before. We can also have that in check that. So we fetch the results from the service. And here we need the term and offset. Okay so we use query get term and query get cursor. There's a tiny thing that we should care for which is that the cursor returns an integer null or string and the search here expects an integer. So let's add a type cost. Okay now our results are is our own class the Wikipedia search result. And we have to translate this into a search result object. Here we use the paginated variant and the name we get from here. Then we need entries which means we have to construct new instances of search result entry. We can map our own articles to that. You find them in the result object and here we also need the cursor which is our offset. That should be the one from the result of course. To translate the articles into search results we create those objects. So thumbnail we don't have one. As title we use the article title. As a subline for each entry you see the main title and then there is another line below. And I'd say we just use some explanatory text. Let's say find more on Wikipedia. As a URL we of course use the article URL. And icon let's just use icon info which looks nice and makes sense for Wikipedia article. Yeah there's one more entry the rounded flag but we only make sense if you have thumbnails which we don't. Okay so now we have our provider but nextcloud is not aware of it yet because we have to register it. We can do that in the application class. So as I said before there's this iPootstrap which is a new mechanism in nextcloud 20. Basically when nextcloud starts you get the chance to register your services as well as run any other code that you want to run at that point of the life cycle of a nextcloud request. We will use the register context to accuracy all the things that you can register. We'll register a search provider and the class is our Wikipedia search provider and we use the class magic class constant which translates this to the fully qualified class name. Okay that should be it all the code that is necessary to write an integration of the unified search. So there's one thing I forgot to add which is in the application class there should be a constructor maybe here to specify the app name I can just use the constant FID. All right we finished the coding part let's take it for a test. Okay if we go back to the browser reload the page once so the new new provider is also known to the front end we can give this a test search for nextcloud there's a files result and there's wikipedia results you can also see pagination we'll load more entries pretty cool huh. Okay this works nicely but there's one thing that we might want to improve since we are using an external API for the search every user input will be sent to wikipedia and that's not always what we want so let's let's take that one step further and add a mechanism that only searches wikipedia if the user types in wiki before the search term to make sure that our search is only sent to wikipedia if the user prefixes it with wikipedia we should check the term so with throwing position we can check with which position wiki shows up in the term and the space so if that isn't zero so if the term doesn't start with wiki and space then we will just return empty result set but if we continue we should also remove the wiki prefix otherwise the search to wikipedia will always include wiki okay we did our change so let's test it if we search for next cloud again we don't get any result from wikipedia but if we prefix with wiki boom there are the wiki results pretty cool okay that's our search app easy wasn't it you can also find documentation online about the process that i just explained and you can look at the other apps that already have unified search integration which you can use as a template if you have any questions about this process or how you can integrate your app you can either use the chat to ask the question or simply open a new topic on the dev section of our forums thanks