8 Example - Reference Documentation
Authors: Noam Y. Tenne, Manuarii Stein, Stephane Maldini, Serge P. Nekoval, Marcos Carceles
Version: 0.0.4.6
Table of Contents
8 Example
8.1 Tweets
8.1.1 The domains
class Tweet { static searchable = { message boost:2.0 } static belongsTo = [ user:User ] static hasMany = [ tags:Tag ] static constraints = { tags nullable:true, cascade:'save, update' } String message = '' Date dateCreated = new Date() }
class User { static searchable = { except = 'password' lastname boost:20 firstname boost:15, index:'not_analyzed' listOfThings index:'no' someThings index:'no' tweets component:true } static constraints = { tweets cascade:'all' } static hasMany = [ tweets:Tweet ] static mappedBy = [ tweets:'user' ] String lastname String firstname String password String activity = 'Evildoer' String someThings = 'something' ArrayList<String> listOfThings = ['this', 'that', 'andthis'] }
class Tag { static searchable = { except=['boostValue'] } String name Integer boostValue = 1 }
8.1.2 The controller
A action triggering indexation
ElasticSearchController
(testCaseService
is just dealing with GORM instructions):
class ElasticSearchController { def elasticSearchService def testCaseService def postTweet = { if(!params.user?.id) { flash.notice = "No user selected." redirect(action: 'index') return } User u = User.get(params.user.id) if (!u) { flash.notice = "User not found" redirect(action: 'index') return } // Create tweet testCaseService.addTweet(params.tweet?.message, u, params.tags) flash.notice = "Tweet posted" redirect(action: 'index') } }
User
in the database), new Tweets will be indexed automatically,
and corresponding User
indexed documents will be updated since we have set the tweets
association as component.Searching for Tweets
def searchForUserTweets = { def tweets = Tweet.search("${params.message.search}").searchResults def tweetsMsg = 'Messages : ' tweets.each { tweetsMsg += "<br />Tweet from ${it.user?.firstname} ${it.user?.lastname} : ${it.message} " tweetsMsg += "(tags : ${it.tags?.collect{t -> t.name}})" } flash.notice = tweetsMsg redirect(action: 'index') }
Searching for anything
def searchAll = { def res = elasticSearchService.search("${params.query}").searchResults def resMsg = '<strong>Global search result(s):</strong><br />' res.each { switch(it){ case Tag: resMsg += "<strong>Tag</strong> ${it.name}<br />" break case Tweet: resMsg += "<strong>Tweet</strong> "${it.message}" from ${it.user.firstname} ${it.user.lastname}<br />" break case User: resMsg += "<strong>User</strong> ${it.firstname} ${it.lastname}<br />" break default: resMsg += "<strong>Other</strong> ${it}<br />" break } } flash.notice = resMsg redirect(action:'index') }
8.2 Geo Distance Search
A search for buildings with a geo_distance filter, ordered by distance.8.2.1 Domains
class GeoPoint { Double lat Double lon static searchable = { root false } }
lat
and lon
are mandatory.class Building { String name GeoPoint location static searchable = { location geoPoint: true, component: true } }
8.2.2 Service Methods
Searching for all buildings sorted by distance with 5km radius around geo location (lat=41.141, lon=11.57)def searchForBuildings() { Closure filter = { geo_distance( 'distance': '5km', 'location': [lat: 48.141, lon: 11.57] ) } def sortBuilder = SortBuilders.geoDistanceSort("location"). point(48.141, 11.57). unit(DistanceUnit.KILOMETERS). order(SortOrder.ASC) def result = elasticSearchService.search( [indices: Building, types: Building, sort: sortBuilder], null as Closure, filter) return [results: result.searchResults, distances: result.sort] }
result.sort
.
sort
contains all search values calculated by the ElasticSearch server as a list mapped to the id of the respective domain objectsExample
assert [1:[23, 42], 2: [24, 40]] == result.sort
8.3 Parent/Child mapping
A store with many departmentsclass Store { String name String description = "A description of a store" String owner = "Shopowner" static searchable = true static constraints = { name blank: false description nullable: true owner nullable: false } }class Department { String name Long numberOfProducts Store store static constraints = { numberOfProducts nullable: true } static searchable = { store parent: true, component:true } }
def result = elasticSearchService.search( QueryBuilders.hasParentQuery("store", QueryBuilders.matchQuery("owner", "Shopowner")), null as Closure, [indices: Department, types: Department] )