Elastic Search with Spring PART II

1) Index creation or deletion:


For Nested Type (Index name is Book) and Parent - Child Relationship (Index name is ParChild)

 @RequestMapping(value="/addIndex",method = RequestMethod.POST)
    def addBookIndex(@RequestParam(name = "indexName")String indexName){
        logger.info "******  Adding Book  Index: $indexName in ELASTIC DB......"
        def result = elasticsearchTemplate.createIndex(indexName)
        logger.info "****** Result is: $result "
        result
    }

    @RequestMapping(value="/deleteIndex",method = RequestMethod.POST)
    def deleteBookIndex(@RequestParam(name = "indexName")String indexName){
        logger.info "******  Deleting Book  Index: $indexName in ELASTIC DB......"
        def result = elasticsearchTemplate.deleteIndex(indexName)
        logger.info "****** Result is: $result "
        result
    }

2) Creating Mapping Type


Whenever we save a document the corresponding type (mapping) gets automatically created, however we can do it explicitly as:

For Nested Type:


 @RequestMapping(value="/addMapping",method = RequestMethod.POST)
    def addBookMapping(){
        logger.info "******  Adding Book Mapping in ELASTIC DB......"
        def data = XContentFactory.jsonBuilder().startObject()/*.startObject("book")*/
               /* .startObject("mappings")*/
                .startObject("funcprog")
                .startObject("properties")
                .startObject("author").field("type","nested")
                .startObject("properties")
                .startObject("name")
                .field("type","String").endObject().endObject().endObject()
                .startObject("id").field("type","Long").endObject()
                .startObject("name").field("type","String").endObject()
                .endObject()
                .endObject()
                /*.endObject()*/
                /*.endObject()*/
                .endObject()

        def result = elasticsearchTemplate.putMapping("book","funcprog",data)
        logger.info "****** Result is: $result "
        result
    }

For Parent-Child Relationship:


 @RequestMapping(value="/addMapping2",method = RequestMethod.POST)
    def addHospitalMapping(){
        logger.info "******  Adding Book Mapping in ELASTIC DB......"
        def data = XContentFactory.jsonBuilder().startObject()


                .startObject("doctor")

                .startObject("_parent")
                .field("type","hospital")
                .endObject()



                .startObject("properties")

                .startObject("id")
                .field("type","String")
                .endObject()

                .startObject("parentId")
                .field("type","String")
                .endObject()

                .startObject("docname")
                .field("type","String")
                .endObject()

                .startObject("trade")
                .field("type","String")
                .endObject()

                .endObject()
                .endObject()

                .endObject()



                def result = elasticsearchTemplate.putMapping("parchild","doctor",data)
        logger.info "****** First Result mapping is: $result "

        data = XContentFactory.jsonBuilder().startObject()/*.startObject("book")*/
        /* .startObject("mappings")*/
                .startObject("hospital")
                .startObject("properties")

                .startObject("id")
                .field("type","String")
                .endObject()

                .startObject("hospname")
                .field("type","String")
                .endObject()

                .startObject("location")
                .field("type","String")
                .endObject()

                .endObject()
                .endObject()



                .endObject()




        result = elasticsearchTemplate.putMapping("parchild","hospital",data)
        logger.info "****** Second Result Mapping is: $result "
        result
    }


XContentFactory basically the builder for the JSON structure to be submitted to the elastic search server.
With the mapping we can specify analyzers too along with other customizations.

For parent-child data modelling the child type mapping needs to be specified first along with the parent type under _parent tag, then the parent type mapping needs to be specified.

3) Save a document.


Saving a document basically implies indexing a document. It can be done as:

For Nested Type:


    @RequestMapping(value="/add",method = RequestMethod.POST)
    def addBook(@RequestBody Book book){
        logger.info "******  Adding Book $book in ELASTIC DB......"
        //bookService.addBook(book)
        def indexQuery = new IndexQueryBuilder().withObject(book).withIndexName("book").withType("funcprog").build();
        def result = elasticsearchTemplate.index(indexQuery)
        logger.info "****** Result is: $result "
        result
    }


For Parent-Child Data modelling:


 @RequestMapping(value="/addHospDoc",method = RequestMethod.POST)
    def addHospDoc(@RequestBody Hospital hospital){
        logger.info "******  Adding Hospital $hospital in ELASTIC DB......"
        def indexQuery = new IndexQueryBuilder().withId(hospital.id).withObject(hospital)
                            .withIndexName("parchild").withType("hospital").build();
        def result = elasticsearchTemplate.index(indexQuery)
        logger.info "****** Result (Hospital) is: $result "

        Doctor doctor = new Doctor(id:"child1${hospital.id}",parentId:hospital.id,docname:"Docname1",trade:"trade1")
         indexQuery = new IndexQueryBuilder().withId("child1${hospital.id}").withObject(doctor).withParentId(hospital.id)
                .withIndexName("parchild").withType("doctor").build();
        result = elasticsearchTemplate.index(indexQuery)
        logger.info "****** Result (Doctor) is: $result "

        doctor = new Doctor(id:"child2${hospital.id}",parentId:hospital.id,docname:"Docname11",trade:"trade11")
        indexQuery = new IndexQueryBuilder().withId("child2${hospital.id}").withObject(doctor).withParentId(hospital.id)
                .withIndexName("parchild").withType("doctor").build();

        result = elasticsearchTemplate.index(indexQuery)
        logger.info "****** Result (Doctor) is: $result "
        new JsonBuilder(result).toPrettyString()
    }

Here after we save the parent data the parentId needs to be specified with the child document so that we can fetch the child based on the parent and vice versa.

4) Execute queries on the document.


NativeQuerySearchBuilder lies at the heart of query formation. However StringQuery representing exact Query JSON (with XContentFactory) can also be used. However we have mainly focussed on NativeQuerySearchBuilder. For different types of queries QueryBuilders provide static helper methods like matchQuery(), termQuery(), boolQuery() which aid in building NativeSearchQuery with NativeQuerySearchBuilder.

Let's examine a example with Nested Type:


 @RequestMapping(value="/retrieve",method = RequestMethod.GET)
    def retrieveBookByName(@RequestParam(name = "name")String name) {
        logger.info "Searching with Name: $name"

        def query = XContentFactory.jsonBuilder().startObject().startObject("query")
                .startObject("match_all")
                .endObject()
                .endObject()
                .endObject()



        /*StringQuery stringQuery = new StringQuery(query.string())
        stringQuery.addIndices("book")
        stringQuery.addTypes("funcprog")
        stringQuery.setPageable(new PageRequest(0,3))
        stringQuery.addFields("name")*/

        /**
            Both the StringQuery and NativeQuery (Hurrah!!! Nested Too) is Now working....
         */


        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        //def nativeQuery = nativeSearchQueryBuilder.withIndices("book").withTypes("funcprog").withQuery(matchAllQuery()).build()

        def matchQueryBuilder = matchQuery("author.name","gordon3")
        def boolQueryBuilder = boolQuery().must(matchQueryBuilder)

        def matchQueryBuilder2 = termQuery("name","action")
        def boolQueryBuilder2 = boolQuery().must(matchQueryBuilder2)

        def temQueryBuilder = termQuery("name","222")
        def boolQueryBuilder3 = boolQuery().must(temQueryBuilder)

        def nativeSearchQuery = nativeSearchQueryBuilder.withIndices("book").withTypes("funcprog").
                withQuery(boolQuery().must(nestedQuery("author",boolQueryBuilder)).must(boolQueryBuilder2))
                /*withQuery(temQueryBuilder)*/
                /*withQuery(nestedQuery("author",boolQueryBuilder2))*/
                .build()
        /**
         * Criteria Queria do not support nested querying...
         */



        /*Criteria criteria = new Criteria("name")
        criteria.contains("007").startsWith("Groovy2")
        *//*Criteria criteria2 = new Criteria("author")
        criteria2.and("author.name").startsWith("Godon2")
        criteria.and(criteria2)*//*
        CriteriaQuery criteriaQuery = new CriteriaQuery(criteria)
        criteriaQuery.addIndices("book")
        criteriaQuery.addTypes("funcprog")
        //criteriaQuery.addFields("id")*/


        def result = elasticsearchTemplate.queryForList(nativeSearchQuery,Book)//elasticsearchTemplate.queryForList(criteriaQuery,Book)/*elasticsearchTemplate.queryForPage(stringQuery,Book)*///elasticsearchTemplate.queryForList(stringQuery,Book)//elasticsearchTemplate.queryForObject(stringQuery,Book)/**/
        logger.info "@@@@@@@@ The Result is: $result "
        result
    }

Here we are trying to find Book document, having author name as "gordon3" and having a term "action" in book name. Since we are querying the nested type author so, we have used here nestedQuery() and then we added another predicate with boolean must.

A example with Parent-child Type:

 @RequestMapping(value="/retrieveParChild",method = RequestMethod.GET)
    def retrieveParChild() {
        def matchQueryBuilder = matchQuery("hospname","CMC33")
        //def boolQueryBuilder2 = boolQuery().must(matchQueryBuilder)
        def hasParentQueryBuilder = hasParentQuery("hospital",matchQueryBuilder)

        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();

        def nativeSearchQuery = nativeSearchQueryBuilder.withIndices("parchild").withTypes("hospital","doctor")
                    .withQuery(hasParentQueryBuilder).build()

        def result = elasticsearchTemplate.queryForList(nativeSearchQuery,Doctor)
        logger.info "### The Result is: $result "
        result

    }
 
Like hasParentQuery there is too hasChildQuery

This is a very brief introduction of Spring foot print in the ELASTIC WORLD. Spring Data ElasticSearch does provide a lot of functionalities with all its Builder APIs.

Please feel free to add your comments ad suggestions.

Comments

Popular posts from this blog

Use of @Configurable annotation.

Spring WS - Part 5

Spring WS - Part 4