1+ /**
2+ * A Kendo DataSource that gets its data from ElasticSearch.
3+ *
4+ * Read-only, supports paging, filtering and sorting.
5+ */
6+ ( function ( kendo ) {
7+ 'use strict' ;
8+
9+ var data = kendo . data ;
10+
11+ if ( ! window . moment ) throw new Error ( 'Moment.js has to be loaded for ElasticSearchDataSource to work' ) ;
12+
13+ // Helper functions for conversion of query parameters from Kendo to ElasticSearch format
14+ function arrayify ( myArg ) {
15+ var _argArray = [ ] ;
16+
17+ if ( myArg && myArg . constructor == Array ) {
18+ _argArray = myArg ;
19+ }
20+ else {
21+ if ( ! ( myArg === void 0 ) )
22+ _argArray . push ( myArg ) ;
23+ }
24+
25+ return _argArray ;
26+ }
27+
28+ data . ElasticSearchDataSource = data . DataSource . extend ( {
29+ init : function ( initOptions ) {
30+ var self = this ;
31+
32+ if ( ! initOptions . transport || ! initOptions . transport . read || ! initOptions . transport . read . url )
33+ throw new Error ( 'transport.read.url must be set to use ElasticSearchDataSource' ) ;
34+
35+ initOptions . transport . read . dataType = initOptions . transport . read . dataType || 'json' ;
36+
37+ // Create a map mapping Kendo field names to ElasticSearch field names. We have to allow ElasticSearch field
38+ // names to be different because ES likes an "@" in field names while Kendo fails on that.
39+ var fields = initOptions . schema . model . fields ;
40+ this . _esFieldMap = [ ] ;
41+ for ( var k in fields ) {
42+ if ( fields . hasOwnProperty ( k ) ) {
43+ this . _esFieldMap [ k ] = fields [ k ] . hasOwnProperty ( 'esName' )
44+ ? fields [ k ] . esName
45+ : k ;
46+ }
47+ }
48+
49+ initOptions . transport . parameterMap = function ( data , type ) {
50+ var sortParams = arrayify ( data . sort ) ;
51+
52+ var esParams = {
53+ from : data . skip ,
54+ size : data . take
55+ } ;
56+
57+ if ( sortParams . length > 0 )
58+ esParams . sort = self . _esFieldMap [ sortParams [ 0 ] . field ] + ":" + sortParams [ 0 ] . dir ;
59+
60+ if ( data . filter )
61+ esParams . q = self . _kendoFilterToESParam ( data . filter ) ;
62+
63+ return esParams ;
64+ } ;
65+
66+ var schema = initOptions . schema ;
67+ schema . parse = function ( response ) {
68+ var hits = response . hits . hits ;
69+ var dataItems = [ ] ;
70+ for ( var i = 0 ; i < hits . length ; i ++ ) {
71+ var hitSource = hits [ i ] . _source ;
72+ var dataItem = { } ;
73+
74+ dataItem . id = hits [ i ] . _id ;
75+ for ( var k in self . _esFieldMap ) {
76+ dataItem [ k ] = hitSource [ self . _esFieldMap [ k ] ] ;
77+ }
78+
79+ dataItems . push ( dataItem ) ;
80+ }
81+ return {
82+ total : response . hits . total ,
83+ data : dataItems
84+ } ;
85+ } ;
86+
87+ schema . data = schema . data || 'data' ;
88+ schema . total = schema . total || 'total' ;
89+ schema . model . id = schema . model . id || '_id' ;
90+
91+ initOptions . serverFiltering = initOptions . serverFiltering || true ;
92+ initOptions . serverSorting = initOptions . serverSorting || true ;
93+ initOptions . serverPaging = initOptions . serverPaging || true ;
94+
95+ data . DataSource . fn . init . call ( this , initOptions ) ;
96+ } ,
97+
98+ _kendoFilterToESParam : function ( kendoFilterObj ) {
99+ // there are three possible structures for the kendoFilterObj:
100+ // * {value: "...", operator: "...", field: " ..."}
101+ // * {logic: "...", filters: ...}
102+ // * [ ... ]
103+
104+ if ( kendoFilterObj . operator ) {
105+ return this . _kendoOperatorFilterToESParam ( kendoFilterObj ) ;
106+ } else if ( kendoFilterObj . logic ) {
107+ return this . _kendoFilterListToESParam ( kendoFilterObj . logic , kendoFilterObj . filters ) ;
108+ } else if ( kendoFilterObj . constructor == Array ) {
109+ return this . _kendoFilterListToESParam ( "and" , kendoFilterObj . filters ) ;
110+ } else {
111+ throw new Error ( "Don't know how to turn this Kendo filter object into ElasticSearch search parameter: "
112+ + kendoFilterObj ) ;
113+ }
114+ } ,
115+
116+ _kendoOperatorFilterToESParam : function ( kendoFilterObj ) {
117+ var fieldEscaped = this . _asESParameter ( this . _esFieldMap [ kendoFilterObj . field ] ) ;
118+ var valueEscaped = this . _asESParameter ( kendoFilterObj . value ) ;
119+
120+ var simpleBinaryOperators = {
121+ eq : "" ,
122+ lt : "<" ,
123+ lte : "<=" ,
124+ gt : ">" ,
125+ gte : ">="
126+ } ;
127+
128+ if ( simpleBinaryOperators [ kendoFilterObj . operator ] !== void 0 ) {
129+ var esOperator = simpleBinaryOperators [ kendoFilterObj . operator ] ;
130+ return fieldEscaped + ":" + esOperator + valueEscaped ;
131+ } else {
132+ switch ( kendoFilterObj . operator ) {
133+ case "neq" :
134+ return "NOT (" + fieldEscaped + ":" + valueEscaped + ")" ;
135+ case "contains" :
136+ return fieldEscaped + ":*" + valueEscaped + "*" ;
137+ case "startswith" :
138+ return fieldEscaped + ":" + valueEscaped + "*" ;
139+ case "endswith" :
140+ return fieldEscaped + ":*" + valueEscaped ;
141+ default :
142+ throw new Error ( "Kendo search operator '" + kendoFilterObj . operator + "' is not yet supported" ) ;
143+ }
144+ }
145+ } ,
146+
147+ // logicalConnective can be "and" or "or"
148+ _kendoFilterListToESParam : function ( logicalConnective , filters ) {
149+ var esParams = [ ] ;
150+
151+ for ( var i = 0 ; i < filters . length ; i ++ ) {
152+ esParams . push ( " (" + this . _kendoFilterToESParam ( filters [ i ] ) + ") " ) ;
153+ }
154+
155+ return esParams . join ( logicalConnective . toUpperCase ( ) )
156+ } ,
157+
158+ _asESParameter : function ( value ) {
159+ if ( value . constructor == Date )
160+ value = value . toISOString ( ) ;
161+
162+ return value . replace ( "\\" , "\\\\" ) . replace ( / [ + \- & | ! ( ) \{ } \[ \] ^ : " ~ * ? : \/ ] / g, "\\$&" ) ;
163+ }
164+ } )
165+
166+ } ) ( window . kendo ) ;
0 commit comments