1 /**
2 * Copyright 2016-2017 Couchbase, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 /**
18 * A facet that categorizes hits into numerical ranges (or buckets) provided by the user.
19 */
20 #include "couchbase.h"
21
22 typedef struct {
23 PCBC_ZEND_OBJECT_PRE
24 double boost;
25 char *field;
26 int limit;
27 PCBC_ZVAL ranges; /* array or arrays like ["range-name" => ["min" => ..., "max" => ...], ...] */
28 PCBC_ZEND_OBJECT_POST
29 } pcbc_numeric_range_search_facet_t;
30
31 #if PHP_VERSION_ID >= 70000
pcbc_numeric_range_search_facet_fetch_object(zend_object * obj)32 static inline pcbc_numeric_range_search_facet_t *pcbc_numeric_range_search_facet_fetch_object(zend_object *obj)
33 {
34 return (pcbc_numeric_range_search_facet_t *)((char *)obj - XtOffsetOf(pcbc_numeric_range_search_facet_t, std));
35 }
36 #define Z_NUMERIC_RANGE_SEARCH_FACET_OBJ(zo) (pcbc_numeric_range_search_facet_fetch_object(zo))
37 #define Z_NUMERIC_RANGE_SEARCH_FACET_OBJ_P(zv) (pcbc_numeric_range_search_facet_fetch_object(Z_OBJ_P(zv)))
38 #else
39 #define Z_NUMERIC_RANGE_SEARCH_FACET_OBJ(zo) ((pcbc_numeric_range_search_facet_t *)zo)
40 #define Z_NUMERIC_RANGE_SEARCH_FACET_OBJ_P(zv) \
41 ((pcbc_numeric_range_search_facet_t *)zend_object_store_get_object(zv TSRMLS_CC))
42 #endif
43
44 #define LOGARGS(lvl) LCB_LOG_##lvl, NULL, "pcbc/numeric_search_facet", __FILE__, __LINE__
45
46 zend_class_entry *pcbc_numeric_range_search_facet_ce;
47
48 /* {{{ proto void NumericRangeSearchFacet::__construct() */
PHP_METHOD(NumericRangeSearchFacet,__construct)49 PHP_METHOD(NumericRangeSearchFacet, __construct)
50 {
51 throw_pcbc_exception("Accessing private constructor.", LCB_EINVAL);
52 }
53 /* }}} */
54
55 /* {{{ proto \Couchbase\NumericRangeSearchFacet NumericRangeSearchFacet::addRange(string $name, double $min, double
56 * $max)
57 */
PHP_METHOD(NumericRangeSearchFacet,addRange)58 PHP_METHOD(NumericRangeSearchFacet, addRange)
59 {
60 pcbc_numeric_range_search_facet_t *obj;
61 char *name = NULL;
62 PCBC_ZVAL range;
63 int rv;
64 pcbc_str_arg_size name_len = 0;
65 double min = 0, max = 0;
66 zend_bool min_null = 0, max_null = 0;
67
68 #if PHP_VERSION_ID >= 50600
69 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sd!d!", &name, &name_len, &min, &min_null, &max, &max_null);
70 #else
71 zval *zmin = NULL, *zmax = NULL;
72 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szz", &name, &name_len, &min, &max);
73 if (zmin) {
74 min = Z_DVAL_P(zmin);
75 } else {
76 min_null = 1;
77 }
78 if (zmax) {
79 min = Z_DVAL_P(zmax);
80 } else {
81 min_null = 1;
82 }
83 #endif
84 if (rv == FAILURE) {
85 RETURN_NULL();
86 }
87
88 obj = Z_NUMERIC_RANGE_SEARCH_FACET_OBJ_P(getThis());
89
90 PCBC_ZVAL_ALLOC(range);
91 array_init_size(PCBC_P(range), 3);
92 ADD_ASSOC_STRINGL(PCBC_P(range), "name", name, name_len);
93 if (!min_null) {
94 ADD_ASSOC_DOUBLE_EX(PCBC_P(range), "min", min);
95 }
96 if (!max_null) {
97 ADD_ASSOC_DOUBLE_EX(PCBC_P(range), "max", max);
98 }
99 add_next_index_zval(PCBC_P(obj->ranges), PCBC_P(range));
100
101 RETURN_ZVAL(getThis(), 1, 0);
102 } /* }}} */
103
104 /* {{{ proto array NumericRangeSearchFacet::jsonSerialize()
105 */
PHP_METHOD(NumericRangeSearchFacet,jsonSerialize)106 PHP_METHOD(NumericRangeSearchFacet, jsonSerialize)
107 {
108 pcbc_numeric_range_search_facet_t *obj;
109 int rv;
110
111 rv = zend_parse_parameters_none();
112 if (rv == FAILURE) {
113 RETURN_NULL();
114 }
115
116 obj = Z_NUMERIC_RANGE_SEARCH_FACET_OBJ_P(getThis());
117 array_init(return_value);
118 ADD_ASSOC_STRING(return_value, "field", obj->field);
119 ADD_ASSOC_LONG_EX(return_value, "size", obj->limit);
120 ADD_ASSOC_ZVAL_EX(return_value, "numeric_ranges", PCBC_P(obj->ranges));
121 PCBC_ADDREF_P(PCBC_P(obj->ranges));
122 } /* }}} */
123
124 ZEND_BEGIN_ARG_INFO_EX(ai_NumericRangeSearchFacet_none, 0, 0, 0)
125 ZEND_END_ARG_INFO()
126
127 ZEND_BEGIN_ARG_INFO_EX(ai_NumericRangeSearchFacet_addRange, 0, 0, 3)
128 ZEND_ARG_INFO(0, name)
129 ZEND_ARG_INFO(0, min)
130 ZEND_ARG_INFO(0, max)
131 ZEND_END_ARG_INFO()
132
133 // clang-format off
134 zend_function_entry numeric_search_facet_methods[] = {
135 PHP_ME(NumericRangeSearchFacet, __construct, ai_NumericRangeSearchFacet_none, ZEND_ACC_PRIVATE | ZEND_ACC_FINAL | ZEND_ACC_CTOR)
136 PHP_ME(NumericRangeSearchFacet, jsonSerialize, ai_NumericRangeSearchFacet_none, ZEND_ACC_PUBLIC)
137 PHP_ME(NumericRangeSearchFacet, addRange, ai_NumericRangeSearchFacet_addRange, ZEND_ACC_PUBLIC)
138 PHP_FE_END
139 };
140 // clang-format on
141
pcbc_numeric_range_search_facet_init(zval * return_value,char * field,int field_len,int limit TSRMLS_DC)142 void pcbc_numeric_range_search_facet_init(zval *return_value, char *field, int field_len, int limit TSRMLS_DC)
143 {
144 pcbc_numeric_range_search_facet_t *obj;
145
146 object_init_ex(return_value, pcbc_numeric_range_search_facet_ce);
147 obj = Z_NUMERIC_RANGE_SEARCH_FACET_OBJ_P(return_value);
148 obj->field = estrndup(field, field_len);
149 obj->limit = limit;
150
151 PCBC_ZVAL_ALLOC(obj->ranges);
152 array_init(PCBC_P(obj->ranges));
153 }
154
155 zend_object_handlers numeric_search_facet_handlers;
156
numeric_search_facet_free_object(pcbc_free_object_arg * object TSRMLS_DC)157 static void numeric_search_facet_free_object(pcbc_free_object_arg *object TSRMLS_DC) /* {{{ */
158 {
159 pcbc_numeric_range_search_facet_t *obj = Z_NUMERIC_RANGE_SEARCH_FACET_OBJ(object);
160
161 if (obj->field != NULL) {
162 efree(obj->field);
163 }
164 zval_ptr_dtor(&obj->ranges);
165
166 zend_object_std_dtor(&obj->std TSRMLS_CC);
167 #if PHP_VERSION_ID < 70000
168 efree(obj);
169 #endif
170 } /* }}} */
171
numeric_search_facet_create_object(zend_class_entry * class_type TSRMLS_DC)172 static pcbc_create_object_retval numeric_search_facet_create_object(zend_class_entry *class_type TSRMLS_DC)
173 {
174 pcbc_numeric_range_search_facet_t *obj = NULL;
175
176 obj = PCBC_ALLOC_OBJECT_T(pcbc_numeric_range_search_facet_t, class_type);
177
178 zend_object_std_init(&obj->std, class_type TSRMLS_CC);
179 object_properties_init(&obj->std, class_type);
180
181 #if PHP_VERSION_ID >= 70000
182 obj->std.handlers = &numeric_search_facet_handlers;
183 return &obj->std;
184 #else
185 {
186 zend_object_value ret;
187 ret.handle = zend_objects_store_put(obj, (zend_objects_store_dtor_t)zend_objects_destroy_object,
188 numeric_search_facet_free_object, NULL TSRMLS_CC);
189 ret.handlers = &numeric_search_facet_handlers;
190 return ret;
191 }
192 #endif
193 }
194
pcbc_numeric_range_search_facet_get_debug_info(zval * object,int * is_temp TSRMLS_DC)195 static HashTable *pcbc_numeric_range_search_facet_get_debug_info(zval *object, int *is_temp TSRMLS_DC) /* {{{ */
196 {
197 pcbc_numeric_range_search_facet_t *obj = NULL;
198 #if PHP_VERSION_ID >= 70000
199 zval retval;
200 #else
201 zval retval = zval_used_for_init;
202 #endif
203
204 *is_temp = 1;
205 obj = Z_NUMERIC_RANGE_SEARCH_FACET_OBJ_P(object);
206
207 array_init(&retval);
208 ADD_ASSOC_STRING(&retval, "field", obj->field);
209 ADD_ASSOC_LONG_EX(&retval, "limit", obj->limit);
210 ADD_ASSOC_ZVAL_EX(&retval, "numeric_ranges", PCBC_P(obj->ranges));
211 PCBC_ADDREF_P(PCBC_P(obj->ranges));
212 return Z_ARRVAL(retval);
213 } /* }}} */
214
PHP_MINIT_FUNCTION(NumericRangeSearchFacet)215 PHP_MINIT_FUNCTION(NumericRangeSearchFacet)
216 {
217 zend_class_entry ce;
218
219 INIT_NS_CLASS_ENTRY(ce, "Couchbase", "NumericRangeSearchFacet", numeric_search_facet_methods);
220 pcbc_numeric_range_search_facet_ce = zend_register_internal_class(&ce TSRMLS_CC);
221 pcbc_numeric_range_search_facet_ce->create_object = numeric_search_facet_create_object;
222 PCBC_CE_DISABLE_SERIALIZATION(pcbc_numeric_range_search_facet_ce);
223
224 zend_class_implements(pcbc_numeric_range_search_facet_ce TSRMLS_CC, 1, pcbc_json_serializable_ce);
225 zend_class_implements(pcbc_numeric_range_search_facet_ce TSRMLS_CC, 1, pcbc_search_facet_ce);
226
227 memcpy(&numeric_search_facet_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
228 numeric_search_facet_handlers.get_debug_info = pcbc_numeric_range_search_facet_get_debug_info;
229 #if PHP_VERSION_ID >= 70000
230 numeric_search_facet_handlers.free_obj = numeric_search_facet_free_object;
231 numeric_search_facet_handlers.offset = XtOffsetOf(pcbc_numeric_range_search_facet_t, std);
232 #endif
233
234 zend_register_class_alias("\\CouchbaseNumericRangeSearchFacet", pcbc_numeric_range_search_facet_ce);
235 return SUCCESS;
236 }
237