Skip to content

Instantly share code, notes, and snippets.

@dengshilong
Last active August 29, 2015 14:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dengshilong/f7a6491d3d4a7bc863af to your computer and use it in GitHub Desktop.
Save dengshilong/f7a6491d3d4a7bc863af to your computer and use it in GitHub Desktop.
发现sphinxclient.c的api竟然没有flush索引到硬盘的功能,于是写了增加了flush功能的sphinxclient.c
//
// $Id: sphinxclient.c 4097 2013-08-20 09:28:24Z kevg $
//
//
// Copyright (c) 2001-2013, Andrew Aksyonoff
// Copyright (c) 2008-2013, Sphinx Technologies Inc
// All rights reserved
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU Library General Public License. You should
// have received a copy of the LGPL license along with this program; if you
// did not, you can find it at http://www.gnu.org/
//
#ifdef _WIN32
#if _MSC_VER>=1400
// VS 2005 and above
#define _CRT_SECURE_NO_DEPRECATE 1
#define _CRT_NONSTDC_NO_DEPRECATE 1
#else
// VS 2003 and below
#define vsnprintf _vsnprintf
#endif
#endif
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#ifndef _WIN32
#include "sphinxclient_config.h"
#endif
#include "sphinxclient.h"
#if _WIN32
// Win-specific headers, calls, libraries
#include <io.h>
#include <winsock2.h>
#pragma comment(linker, "/defaultlib:wsock32.lib")
#pragma message("Automatically linking with wsock32.lib")
#define EWOULDBLOCK WSAEWOULDBLOCK
#define EINTR WSAEINTR
#else
// UNIX-specific headers and calls
#include <unistd.h>
#include <netinet/in.h>
#include <sys/file.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <netdb.h>
#include <errno.h>
#include <sys/un.h>
#include <sys/fcntl.h>
#endif
//////////////////////////////////////////////////////////////////////////
#define MAX_REQS 32
#define CONNECT_TIMEOUT_MSEC 1000
#define MAX_PACKET_LEN (8*1024*1024)
enum
{
SEARCHD_COMMAND_SEARCH = 0,
SEARCHD_COMMAND_EXCERPT = 1,
SEARCHD_COMMAND_UPDATE = 2,
SEARCHD_COMMAND_KEYWORDS = 3,
SEARCHD_COMMAND_PERSIST = 4,
SEARCHD_COMMAND_STATUS = 5,
SEARCHD_COMMAND_FLUSHATTRS = 7
};
enum
{
VER_COMMAND_EXCERPT = 0x103,
VER_COMMAND_UPDATE = 0x102,
VER_COMMAND_KEYWORDS = 0x100,
VER_COMMAND_STATUS = 0x101,
VER_COMMAND_FLUSHATTRS = 0x100
};
//////////////////////////////////////////////////////////////////////////
struct st_filter
{
const char * attr;
int filter_type;
int num_values;
const sphinx_int64_t * values;
sphinx_int64_t umin;
sphinx_int64_t umax;
float fmin;
float fmax;
int exclude;
const char * svalue;
};
union un_attr_value
{
sphinx_int64_t int_value;
float float_value;
unsigned int * mva_value;
const char * string;
};
struct st_override
{
const char * attr;
const sphinx_uint64_t * docids;
int num_values;
const unsigned int * uint_values;
};
struct st_sphinx_client
{
unsigned short ver_search; ///< compatibility mode
sphinx_bool copy_args; ///< whether to create a copy of each passed argument
void * head_alloc; ///< head of client-owned allocations list
const char * error; ///< last error
const char * warning; ///< last warning
char local_error_buf[256]; ///< buffer to store 'local' error messages (eg. connect() error)
const char * host;
int port;
float timeout;
int offset;
int limit;
int mode;
int num_weights;
const int * weights;
int sort;
const char * sortby;
sphinx_uint64_t minid;
sphinx_uint64_t maxid;
const char * group_by;
int group_func;
const char * group_sort;
const char * group_distinct;
int max_matches;
int cutoff;
int retry_count;
int retry_delay;
const char * geoanchor_attr_lat;
const char * geoanchor_attr_long;
float geoanchor_lat;
float geoanchor_long;
int num_filters;
int max_filters;
struct st_filter * filters;
int num_index_weights;
const char ** index_weights_names;
const int * index_weights_values;
int ranker;
const char * rankexpr;
int max_query_time;
int num_field_weights;
const char ** field_weights_names;
const int * field_weights_values;
int num_overrides;
int max_overrides;
struct st_override * overrides;
const char * select_list;
int query_flags;
int predicted_time;
const char * outer_orderby;
int outer_offset;
int outer_limit;
sphinx_bool has_outer;
int num_reqs;
int req_lens [ MAX_REQS ];
char * reqs [ MAX_REQS ];
int response_len;
char * response_buf; ///< where the buffer begins (might also contain heading warning)
char * response_start; ///< where the data to parse starts
int num_results;
sphinx_result results [ MAX_REQS ];
int sock; ///< open socket for pconns; -1 if none
sphinx_bool persist;
};
//////////////////////////////////////////////////////////////////////////
static void * chain ( sphinx_client * client, const void * ptr, size_t len );
static const char * strchain ( sphinx_client * client, const char * s );
static void unchain ( sphinx_client * client, const void * ptr );
static void unchain_all ( sphinx_client * client );
sphinx_client * sphinx_create ( sphinx_bool copy_args )
{
sphinx_client * client;
int i;
// allocate
client = malloc ( sizeof(sphinx_client) );
if ( !client )
return NULL;
// initialize defaults and return
client->ver_search = 0x11E; // 0x113 for 0.9.8, 0x116 for 0.9.9rc2
client->copy_args = copy_args;
client->head_alloc = NULL;
client->error = NULL;
client->warning = NULL;
client->local_error_buf[0] = '\0';
client->host = strchain ( client, "localhost" );
client->port = 9312;
client->timeout = 0.0f;
client->offset = 0;
client->limit = 20;
client->mode = SPH_MATCH_EXTENDED2;
client->num_weights = 0;
client->weights = NULL;
client->sort = SPH_SORT_RELEVANCE;
client->sortby = NULL;
client->minid = 0;
client->maxid = 0;
client->group_by = NULL;
client->group_func = SPH_GROUPBY_ATTR;
client->group_sort = strchain ( client, "@groupby desc" );
client->group_distinct = NULL;
client->max_matches = 1000;
client->cutoff = 0;
client->retry_count = 0;
client->retry_delay = 0;
client->geoanchor_attr_lat = NULL;
client->geoanchor_attr_long = NULL;
client->geoanchor_lat = 0.0f;
client->geoanchor_long = 0.0f;
client->num_filters = 0;
client->max_filters = 0;
client->filters = NULL;
client->num_index_weights = 0;
client->index_weights_names = NULL;
client->index_weights_values = NULL;
client->ranker = SPH_RANK_DEFAULT;
client->rankexpr = NULL;
client->max_query_time = 0;
client->num_field_weights = 0;
client->field_weights_names = NULL;
client->field_weights_values = NULL;
client->num_overrides = 0;
client->max_overrides = 0;
client->overrides = NULL;
client->select_list = NULL;
client->query_flags = 1<<6;
client->predicted_time = 0;
client->outer_orderby = NULL;
client->outer_offset = 0;
client->outer_limit = 0;
client->has_outer = SPH_FALSE;
client->num_reqs = 0;
client->response_len = 0;
client->response_buf = NULL;
client->num_results = 0;
for ( i=0; i<MAX_REQS; i++ )
{
client->results[i].values_pool = NULL;
client->results[i].words = NULL;
client->results[i].fields = NULL;
client->results[i].attr_names = NULL;
client->results[i].attr_types = NULL;
}
client->sock = -1;
client->persist = SPH_FALSE;
return client;
}
static void sphinx_free_results ( sphinx_client * client )
{
int i;
for ( i=0; i<client->num_results; i++ )
{
free ( client->results[i].values_pool );
free ( client->results[i].words );
free ( client->results[i].fields );
free ( client->results[i].attr_names );
free ( client->results[i].attr_types );
client->results[i].values_pool = NULL;
client->results[i].words = NULL;
client->results[i].fields = NULL;
client->results[i].attr_names = NULL;
client->results[i].attr_types = NULL;
}
client->num_results = 0;
}
void sock_close ( int sock );
#define safe_free(_ptr) \
if ( _ptr ) \
{ \
free ( _ptr ); \
_ptr = NULL; \
}
void sphinx_cleanup ( sphinx_client * client )
{
int i;
if ( !client )
return;
for ( i=0; i<client->num_reqs; i++ )
safe_free ( client->reqs[i] );
client->num_reqs = 0;
sphinx_free_results ( client );
client->num_results = 0;
client->num_results = 0;
safe_free ( client->response_buf );
}
void sphinx_destroy ( sphinx_client * client )
{
int i;
if ( !client )
return;
for ( i=0; i<client->num_reqs; i++ )
safe_free ( client->reqs[i] );
sphinx_free_results ( client );
unchain_all ( client );
safe_free ( client->filters );
safe_free ( client->response_buf );
if ( client->sock>=0 )
sock_close ( client->sock );
free ( client );
}
const char * sphinx_error ( sphinx_client * client )
{
return client->error ? client->error : "";
}
const char * sphinx_warning ( sphinx_client * client )
{
return client->warning ? client->warning : "";
}
static void set_error ( sphinx_client * client, const char * template, ... )
{
va_list ap;
if ( !client )
return;
va_start ( ap, template );
vsnprintf ( client->local_error_buf, sizeof(client->local_error_buf), template, ap );
va_end ( ap );
client->error = client->local_error_buf;
client->warning = NULL;
}
//////////////////////////////////////////////////////////////////////////
struct st_memblock
{
struct st_memblock * prev;
struct st_memblock * next;
};
static void * chain ( sphinx_client * client, const void * ptr, size_t len )
{
struct st_memblock * entry;
if ( !client->copy_args || !ptr )
return (void*) ptr;
entry = malloc ( sizeof(struct st_memblock) + len );
if ( !entry )
{
set_error ( client, "malloc() failed (bytes=%d)", sizeof(struct st_memblock) + len );
return NULL;
}
entry->prev = NULL;
entry->next = client->head_alloc;
if ( entry->next )
entry->next->prev = entry;
client->head_alloc = entry;
entry++;
memcpy ( entry, ptr, len );
return entry;
}
static const char * strchain ( sphinx_client * client, const char * s )
{
return s ? chain ( client, s, 1+strlen(s) ) : NULL;
}
static void unchain ( sphinx_client * client, const void * ptr )
{
struct st_memblock * entry;
if ( !client->copy_args || !ptr )
return;
entry = (struct st_memblock*) ptr;
entry--;
if ( entry->prev )
entry->prev->next = entry->next;
else
client->head_alloc = entry->next;
if ( entry->next )
entry->next->prev = entry->prev;
free ( entry );
}
static void unchain_all ( sphinx_client * client )
{
struct st_memblock *to_free, *cur;
if ( !client || !client->copy_args )
return;
cur = client->head_alloc;
while ( cur )
{
to_free = cur;
cur = cur->next;
free ( to_free );
}
client->head_alloc = NULL;
}
//////////////////////////////////////////////////////////////////////////
sphinx_bool sphinx_set_server ( sphinx_client * client, const char * host, int port )
{
if ( !client || !host || !host[0] )
{
set_error ( client, "invalid arguments (host must not be empty)" );
return SPH_FALSE;
}
unchain ( client, client->host );
client->host = strchain ( client, host );
client->port = port;
return SPH_TRUE;
}
sphinx_bool sphinx_set_connect_timeout ( sphinx_client * client, float seconds )
{
if ( !client )
return SPH_FALSE;
client->timeout = seconds;
return SPH_TRUE;
}
sphinx_bool sphinx_set_limits ( sphinx_client * client, int offset, int limit, int max_matches, int cutoff )
{
if ( !client || offset<0 || limit<=0 || max_matches<0 || cutoff<0 )
{
if ( offset<0 ) set_error ( client, "invalid arguments (offset must be >= 0)" );
else if ( limit<=0 ) set_error ( client, "invalid arguments (limit must be > 0)" );
else if ( max_matches<0 ) set_error ( client, "invalid arguments (max_matches must be >= 0)" );
else if ( cutoff<0 ) set_error ( client, "invalid arguments (cutoff must be >= 0)" );
else set_error ( client, "invalid arguments" );
return SPH_FALSE;
}
client->offset = offset;
client->limit = limit;
if ( max_matches>=0 )
client->max_matches = max_matches;
if ( cutoff>=0 )
client->cutoff = cutoff;
return SPH_TRUE;
}
sphinx_bool sphinx_set_max_query_time ( sphinx_client * client, int max_query_time )
{
if ( !client || max_query_time<=0 )
{
set_error ( client, "invalid arguments (max_query_time must be > 0)" );
return SPH_FALSE;
}
client->max_query_time = max_query_time;
return SPH_TRUE;
}
sphinx_bool sphinx_set_match_mode ( sphinx_client * client, int mode )
{
if ( !client || mode<SPH_MATCH_ALL || mode>SPH_MATCH_EXTENDED2 ) // FIXME?
{
set_error ( client, "invalid arguments (matching mode %d out of bounds)", mode );
return SPH_FALSE;
}
client->mode = mode;
return SPH_TRUE;
}
sphinx_bool sphinx_set_ranking_mode ( sphinx_client * client, int ranker, const char * rankexpr )
{
if ( !client || ranker<SPH_RANK_PROXIMITY_BM25 || ranker>=SPH_RANK_TOTAL ) // FIXME?
{
set_error ( client, "invalid arguments (ranking mode %d out of bounds)", ranker );
return SPH_FALSE;
}
client->ranker = ranker;
client->rankexpr = strchain ( client, rankexpr );
return SPH_TRUE;
}
sphinx_bool sphinx_set_sort_mode ( sphinx_client * client, int mode, const char * sortby )
{
if ( !client
|| mode<SPH_SORT_RELEVANCE
|| mode>SPH_SORT_EXPR
|| ( mode!=SPH_SORT_RELEVANCE && ( !sortby || !sortby[0] ) ) )
{
if ( mode<SPH_SORT_RELEVANCE || mode>SPH_SORT_EXPR )
{
set_error ( client, "invalid arguments (sorting mode %d out of bounds)", mode );
} else if ( mode!=SPH_SORT_RELEVANCE && ( !sortby || !sortby[0] ) )
{
set_error ( client, "invalid arguments (sortby clause must not be empty)", mode );
} else
{
set_error ( client, "invalid arguments", mode );
}
return SPH_FALSE;
}
client->sort = mode;
unchain ( client, client->sortby );
client->sortby = strchain ( client, sortby );
return SPH_TRUE;
}
sphinx_bool sphinx_set_field_weights ( sphinx_client * client, int num_weights, const char ** field_names, const int * field_weights )
{
int i;
if ( !client || num_weights<=0 || !field_names || !field_weights )
{
if ( num_weights<=0 ) set_error ( client, "invalid arguments (num_weights must be > 0)" );
else if ( !field_names ) set_error ( client, "invalid arguments (field_names must not be NULL)" );
else if ( !field_names ) set_error ( client, "invalid arguments (field_weights must not be NULL)" );
else set_error ( client, "invalid arguments" );
return SPH_FALSE;
}
if ( client->copy_args )
{
for ( i=0; i<client->num_field_weights; i++ )
unchain ( client, client->field_weights_names[i] );
unchain ( client, client->field_weights_names );
unchain ( client, client->field_weights_values );
field_names = chain ( client, field_names, num_weights*sizeof(const char*) );
for ( i=0; i<num_weights; i++ )
field_names[i] = strchain ( client, field_names[i] );
field_weights = chain ( client, field_weights, num_weights*sizeof(int) );
}
client->num_field_weights = num_weights;
client->field_weights_names = field_names;
client->field_weights_values = field_weights;
return SPH_TRUE;
}
sphinx_bool sphinx_set_index_weights ( sphinx_client * client, int num_weights, const char ** index_names, const int * index_weights )
{
int i;
if ( !client || num_weights<=0 || !index_names || !index_weights )
{
if ( num_weights<=0 ) set_error ( client, "invalid arguments (num_weights must be > 0)" );
else if ( !index_names ) set_error ( client, "invalid arguments (index_names must not be NULL)" );
else if ( !index_names ) set_error ( client, "invalid arguments (index_weights must not be NULL)" );
else set_error ( client, "invalid arguments" );
return SPH_FALSE;
}
if ( client->copy_args )
{
for ( i=0; i<client->num_index_weights; i++ )
unchain ( client, client->index_weights_names[i] );
unchain ( client, client->index_weights_names );
unchain ( client, client->index_weights_values );
index_names = chain ( client, index_names, num_weights*sizeof(const char*) );
for ( i=0; i<num_weights; i++ )
index_names[i] = strchain ( client, index_names[i] );
index_weights = chain ( client, index_weights, num_weights*sizeof(int) );
}
client->num_index_weights = num_weights;
client->index_weights_names = index_names;
client->index_weights_values = index_weights;
return SPH_TRUE;
}
sphinx_bool sphinx_set_id_range ( sphinx_client * client, sphinx_uint64_t minid, sphinx_uint64_t maxid )
{
if ( !client || minid>maxid )
{
set_error ( client, "invalid arguments (minid must be <= maxid)" );
return SPH_FALSE;
}
client->minid = minid;
client->maxid = maxid;
return SPH_TRUE;
}
static struct st_filter * sphinx_add_filter_entry ( sphinx_client * client )
{
int len;
if ( client->num_filters>=client->max_filters )
{
len = ( client->max_filters<=0 ) ? client->num_filters + 8 : 2*client->max_filters;
len *= sizeof(struct st_filter);
client->filters = realloc ( client->filters, len );
if ( !client->filters )
{
set_error ( client, "realloc() failed (bytes=%d)", len );
return NULL;
}
}
return client->filters + client->num_filters++;
}
sphinx_bool sphinx_add_filter ( sphinx_client * client, const char * attr, int num_values, const sphinx_int64_t * values, sphinx_bool exclude )
{
struct st_filter * filter;
if ( !client || !attr || num_values<=0 || !values )
{
if ( !attr ) set_error ( client, "invalid arguments (attr must not be empty)" );
else if ( num_values<=0 ) set_error ( client, "invalid arguments (num_values must be > 0)" );
else if ( !values ) set_error ( client, "invalid arguments (values must not be NULL)" );
else set_error ( client, "invalid arguments" );
return SPH_FALSE;
}
filter = sphinx_add_filter_entry ( client );
if ( !filter )
return SPH_FALSE;
filter->attr = strchain ( client, attr );
filter->filter_type = SPH_FILTER_VALUES;
filter->num_values = num_values;
filter->values = chain ( client, values, num_values*sizeof(sphinx_int64_t) );
filter->exclude = exclude;
return SPH_TRUE;
}
sphinx_bool sphinx_add_filter_string ( sphinx_client * client, const char * attr, const char * value, sphinx_bool exclude )
{
struct st_filter * filter;
if ( !client || !attr || !value )
{
if ( !attr ) set_error ( client, "invalid arguments (attr must not be empty)" );
else if ( !value ) set_error ( client, "invalid arguments (value must not be empty)" );
else set_error ( client, "invalid arguments" );
return SPH_FALSE;
}
filter = sphinx_add_filter_entry ( client );
if ( !filter )
return SPH_FALSE;
filter->attr = strchain ( client, attr );
filter->filter_type = SPH_FILTER_STRING;
filter->svalue = strchain ( client, value );
filter->exclude = exclude;
return SPH_TRUE;
}
sphinx_bool sphinx_add_filter_range ( sphinx_client * client, const char * attr, sphinx_int64_t umin, sphinx_int64_t umax, sphinx_bool exclude )
{
struct st_filter * filter;
if ( !client || !attr || umin>umax )
{
if ( !attr ) set_error ( client, "invalid arguments (attr must not be empty)" );
else if ( umin>umax ) set_error ( client, "invalid arguments (umin must be <= umax)" );
else set_error ( client, "invalid arguments" );
return SPH_FALSE;
}
filter = sphinx_add_filter_entry ( client );
if ( !filter )
return SPH_FALSE;
filter->attr = strchain ( client, attr );
filter->filter_type = SPH_FILTER_RANGE;
filter->umin = umin;
filter->umax = umax;
filter->exclude = exclude;
return SPH_TRUE;
}
sphinx_bool sphinx_add_filter_float_range ( sphinx_client * client, const char * attr, float fmin, float fmax, sphinx_bool exclude )
{
struct st_filter * filter;
if ( !client || !attr || fmin>fmax )
{
if ( !attr ) set_error ( client, "invalid arguments (attr must not be empty)" );
else if ( fmin>fmax ) set_error ( client, "invalid arguments (fmin must be <= fmax)" );
else set_error ( client, "invalid arguments" );
return SPH_FALSE;
}
filter = sphinx_add_filter_entry ( client );
if ( !filter )
return SPH_FALSE;
filter->attr = strchain ( client, attr );
filter->filter_type = SPH_FILTER_FLOATRANGE;
filter->fmin = fmin;
filter->fmax = fmax;
filter->exclude = exclude;
return SPH_TRUE;
}
sphinx_bool sphinx_set_geoanchor ( sphinx_client * client, const char * attr_latitude, const char * attr_longitude, float latitude, float longitude )
{
if ( !client || !attr_latitude || !attr_latitude[0] || !attr_longitude || !attr_longitude[0] )
{
if ( !attr_latitude || !attr_latitude[0] ) set_error ( client, "invalid arguments (attr_latitude must not be empty)" );
else if ( !attr_longitude || !attr_longitude[0] ) set_error ( client, "invalid arguments (attr_longitude must not be empty)" );
else set_error ( client, "invalid arguments" );
return SPH_FALSE;
}
unchain ( client, client->geoanchor_attr_lat );
unchain ( client, client->geoanchor_attr_long );
client->geoanchor_attr_lat = strchain ( client, attr_latitude );
client->geoanchor_attr_long = strchain ( client, attr_longitude );
client->geoanchor_lat = latitude;
client->geoanchor_long = longitude;
return SPH_TRUE;
}
sphinx_bool sphinx_set_groupby ( sphinx_client * client, const char * attr, int groupby_func, const char * group_sort )
{
if ( !client || !attr || groupby_func<SPH_GROUPBY_DAY || groupby_func>SPH_GROUPBY_ATTRPAIR )
{
if ( !attr )
{
set_error ( client, "invalid arguments (attr must not be empty)" );
} else if ( groupby_func<SPH_GROUPBY_DAY || groupby_func>SPH_GROUPBY_ATTRPAIR )
{
set_error ( client, "invalid arguments (groupby_func %d out of bounds)", groupby_func );
} else
{
set_error ( client, "invalid arguments" );
}
return SPH_FALSE;
}
unchain ( client, client->group_by );
unchain ( client, client->group_sort );
client->group_by = strchain ( client, attr );
client->group_func = groupby_func;
client->group_sort = strchain ( client, group_sort ? group_sort : "@groupby desc" );
return SPH_TRUE;
}
sphinx_bool sphinx_set_groupby_distinct ( sphinx_client * client, const char * attr )
{
if ( !client || !attr )
{
if ( !attr ) set_error ( client, "invalid arguments (attr must not be empty)" );
else set_error ( client, "invalid arguments" );
return SPH_FALSE;
}
unchain ( client, client->group_distinct );
client->group_distinct = strchain ( client, attr );
return SPH_TRUE;
}
sphinx_bool sphinx_set_retries ( sphinx_client * client, int count, int delay )
{
if ( !client || count<0 || count>1000 || delay<0 || delay>100000 )
{
if ( count<0 || count>1000 ) set_error ( client, "invalid arguments (count value %d out of bounds)", count );
else if ( delay<0 || delay>100000 ) set_error ( client, "invalid arguments (delay value %d out of bounds)", delay );
else set_error ( client, "invalid arguments" );
return SPH_FALSE;
}
client->retry_count = count;
client->retry_delay = delay;
return SPH_TRUE;
}
sphinx_bool sphinx_add_override ( sphinx_client * client, const char * attr, const sphinx_uint64_t * docids, int num_values, const unsigned int * values )
{
struct st_override * p;
if ( !client )
return SPH_FALSE;
if ( client->ver_search<0x115 )
{
set_error ( client, "sphinx_add_override not supported by chosen protocol version" );
return SPH_FALSE;
}
if ( client->num_overrides>=client->max_overrides )
{
client->max_overrides = ( client->max_overrides<=0 ) ? 8 : 2*client->max_overrides;
client->overrides = realloc ( client->overrides, client->max_overrides *sizeof(struct st_override) );
}
p = client->overrides + client->num_overrides;
client->num_overrides++;
p->attr = strchain ( client, attr );
p->docids = chain ( client, docids, sizeof(sphinx_uint64_t)*num_values );
p->num_values = num_values;
p->uint_values = chain ( client, values, sizeof(unsigned int)*num_values );
return SPH_TRUE;
}
sphinx_bool sphinx_set_select ( sphinx_client * client, const char * select_list )
{
if ( !client )
return SPH_FALSE;
if ( client->ver_search<0x116 )
{
set_error ( client, "sphinx_set_select not supported by chosen protocol version" );
return SPH_FALSE;
}
unchain ( client, client->select_list );
client->select_list = strchain ( client, select_list );
return SPH_TRUE;
}
void set_bit ( int * flags, int bit, sphinx_bool enable )
{
int bit_mask = 1<<bit;
if ( enable )
*flags |= bit_mask;
else
*flags &= ( 0xff ^ bit_mask );
}
sphinx_bool sphinx_set_query_flags ( sphinx_client * client, const char * flag_name, sphinx_bool enabled, int max_predicted_msec )
{
if ( !client )
return SPH_FALSE;
if ( client->ver_search<0x11B )
{
set_error ( client, "sphinx_set_query_flags not supported by chosen protocol version" );
return SPH_FALSE;
}
if ( !flag_name || !flag_name[0] )
{
set_error ( client, "invalid arguments (empty flag_name)" );
return SPH_FALSE;
}
if ( strcmp ( flag_name, "max_predicted_time")==0 && max_predicted_msec<0 )
{
set_error ( client, "invalid arguments (max_predicted_time must be >0)" );
return SPH_FALSE;
}
if ( strcmp ( flag_name, "reverse_scan")==0 )
{
set_bit ( &client->query_flags, 0, enabled );
} else if ( strcmp ( flag_name, "sort_method_kbuffer")==0 )
{
set_bit ( &client->query_flags, 1, enabled );
} else if ( strcmp ( flag_name, "max_predicted_time")==0 )
{
client->predicted_time = max_predicted_msec;
set_bit ( &client->query_flags, 2, max_predicted_msec>0 );
} else if ( strcmp ( flag_name, "boolean_simplify")==0 )
{
set_bit ( &client->query_flags, 3, enabled );
} else if ( strcmp ( flag_name, "idf_plain")==0 )
{
set_bit ( &client->query_flags, 4, enabled );
} else if ( strcmp ( flag_name, "global_idf")==0 )
{
set_bit ( &client->query_flags, 5, enabled );
} else if ( strcmp ( flag_name, "tfidf_normalized")==0 )
{
set_bit ( &client->query_flags, 6, enabled );
} else
{
set_error ( client, "invalid arguments (unknown flag_name)" );
return SPH_FALSE;
}
return SPH_TRUE;
}
void sphinx_reset_query_flags ( sphinx_client * client )
{
client->query_flags = 1<<6;
client->predicted_time = 0;
}
sphinx_bool sphinx_set_outer_select ( sphinx_client * client, const char * orderby, int offset, int limit )
{
if ( !client )
return SPH_FALSE;
if ( client->ver_search<0x11D )
{
set_error ( client, "sphinx_set_outer not supported by chosen protocol version" );
return SPH_FALSE;
}
unchain ( client, client->outer_orderby );
client->outer_orderby = strchain ( client, orderby );
client->outer_offset = offset;
client->outer_limit = limit;
client->has_outer = SPH_TRUE;
return SPH_TRUE;
}
void sphinx_reset_outer_select ( sphinx_client * client )
{
if ( !client )
return;
unchain ( client, client->outer_orderby );
client->outer_orderby = NULL;
client->outer_offset = 0;
client->outer_limit = 0;
client->has_outer = SPH_FALSE;
}
void sphinx_reset_filters ( sphinx_client * client )
{
int i;
if ( !client )
return;
if ( client->filters )
{
if ( client->copy_args )
for ( i=0; i<client->num_filters; i++ )
{
unchain ( client, client->filters[i].attr );
if ( client->filters[i].filter_type==SPH_FILTER_VALUES )
unchain ( client, client->filters[i].values );
if ( client->filters[i].filter_type==SPH_FILTER_STRING )
unchain ( client, client->filters[i].svalue );
}
free ( client->filters );
client->filters = NULL;
}
client->num_filters = client->max_filters = 0;
}
void sphinx_reset_groupby ( sphinx_client * client )
{
if ( !client )
return;
unchain ( client, client->group_by );
unchain ( client, client->group_sort );
client->group_by = NULL;
client->group_func = SPH_GROUPBY_ATTR;
client->group_sort = strchain ( client, "@groupby desc" );
client->group_distinct = NULL;
}
//////////////////////////////////////////////////////////////////////////
static int sphinx_dismiss_requests ( sphinx_client * client )
{
int nreqs = client->num_reqs, i;
for ( i=0; i<client->num_reqs; i++ )
free ( client->reqs[i] );
client->num_reqs = 0;
return nreqs;
}
sphinx_result * sphinx_query ( sphinx_client * client, const char * query, const char * index_list, const char * comment )
{
sphinx_result * res;
if ( !client )
return NULL;
if ( client->num_reqs!=0 )
{
set_error ( client, "sphinx_query() must not be called after sphinx_add_query()" );
return NULL;
}
if ( sphinx_add_query ( client, query, index_list, comment )!=0 )
return NULL;
res = sphinx_run_queries ( client ); // just a shortcut for client->results[0]
sphinx_dismiss_requests ( client ); // sphinx_query() is fire and forget; dismiss request in all cases
if ( !res )
return NULL;
client->error = res->error;
client->warning = res->warning;
return ( res->status==SEARCHD_ERROR ) ? NULL : res;
}
static size_t safestrlen ( const char * s )
{
return s ? strlen(s) : 0;
}
static int calc_req_len ( sphinx_client * client, const char * query, const char * index_list, const char * comment )
{
int i, filter_val_size;
size_t res;
res = 96 + 2*(int)sizeof(sphinx_uint64_t) + 4*client->num_weights
+ safestrlen ( client->sortby )
+ safestrlen ( query )
+ safestrlen ( index_list )
+ safestrlen ( client->group_by )
+ safestrlen ( client->group_sort )
+ safestrlen ( client->group_distinct )
+ safestrlen ( comment )
+ ( ( client->ranker==SPH_RANK_EXPR ) ? ( 4 + safestrlen ( client->rankexpr ) ) : 0 );
filter_val_size = ( client->ver_search>=0x114 ) ? 8 : 4;
for ( i=0; i<client->num_filters; i++ )
{
const struct st_filter * filter = &client->filters[i];
res += 12 + safestrlen ( filter->attr ); // string attr-name; int type; int exclude-flag
switch ( filter->filter_type )
{
case SPH_FILTER_VALUES: res += 4 + filter_val_size*filter->num_values; break; // int values-count; uint32/int64[] values
case SPH_FILTER_RANGE: res += 2*filter_val_size; break; // uint32/int64 min-val, max-val
case SPH_FILTER_FLOATRANGE: res += 8; break; // float min-val,max-val
case SPH_FILTER_STRING: res += 4 + safestrlen ( filter->svalue ); break;
}
}
if ( client->geoanchor_attr_lat && client->geoanchor_attr_long )
res += 16 + safestrlen ( client->geoanchor_attr_lat ) + safestrlen ( client->geoanchor_attr_long ); // string lat-attr, long-attr; float lat, long
for ( i=0; i<client->num_index_weights; i++ )
res += 8 + safestrlen ( client->index_weights_names[i] ); // string index-name; int index-weight
for ( i=0; i<client->num_field_weights; i++ )
res += 8 + safestrlen ( client->field_weights_names[i] ); // string field-name; int field-weight
if ( client->ver_search>=0x115 )
{
res += 4; // int overrides-count
for ( i=0; i<client->num_overrides; i++ )
{
res += 8 + safestrlen ( client->overrides[i].attr ); // string attr, int attr-type
res += 4 + 12*client->overrides[i].num_values; // int values-count, { uint64 docid, uint32 value }[] override
}
}
if ( client->ver_search>=0x116 )
res += 4 + safestrlen ( client->select_list ); // string select_list
if ( client->ver_search>=0x11B )
res += 4 + ( client->predicted_time>0 ? 4 : 0 );
if ( client->ver_search>=0x11D )
res += safestrlen ( client->outer_orderby ) + 16; // string outer order by + int outer offset + int outer limit + has outer flag
return (int)res;
}
static void send_bytes ( char ** pp, const char * bytes, int len )
{
char * ptr;
int i;
ptr = *pp;
if ( ptr )
for ( i=0; i<len; i++ )
*ptr++ = bytes[i];
*pp = ptr;
}
static void send_int ( char ** pp, unsigned int value )
{
unsigned char * b = (unsigned char*) *pp;
b[0] = ( value >> 24 ) & 0xff;
b[1] = ( value >> 16 ) & 0xff;
b[2] = ( value >> 8 ) & 0xff;
b[3] = ( value & 0xFF );
*pp += 4;
}
static void send_word ( char ** pp, unsigned short value )
{
unsigned char * b = (unsigned char*) *pp;
b[0] = ( value >> 8 );
b[1] = ( value & 0xFF );
*pp += 2;
}
static void send_str ( char ** pp, const char * s )
{
int len;
len = s ? (int)strlen(s) : 0;
send_int ( pp, len );
send_bytes ( pp, s, len );
}
static void send_qword ( char ** pp, sphinx_uint64_t value )
{
send_int ( pp, (int)( value >> 32 ) );
send_int ( pp, (int)( value & ((sphinx_uint64_t)0xffffffffL) ) );
}
static void send_float ( char ** pp, float value )
{
union
{
float f;
int i;
} u;
u.f = value;
send_int ( pp, u.i );
}
int sphinx_add_query ( sphinx_client * client, const char * query, const char * index_list, const char * comment )
{
int i, j, req_len;
char * req;
if ( client->num_reqs<0 || client->num_reqs>=MAX_REQS )
{
set_error ( client, "num_reqs=%d out of bounds (too many queries?)", client->num_reqs );
return -1;
}
req_len = calc_req_len ( client, query, index_list, comment );
req = malloc ( req_len );
if ( !req )
{
set_error ( client, "malloc() failed (bytes=%d)", req_len );
return -1;
}
client->reqs[client->num_reqs] = req;
client->req_lens[client->num_reqs] = req_len;
client->num_reqs++;
if ( client->ver_search>=0x11B )
send_int ( &req, client->query_flags );
send_int ( &req, client->offset );
send_int ( &req, client->limit );
send_int ( &req, client->mode );
send_int ( &req, client->ranker );
if ( client->ranker==SPH_RANK_EXPR )
send_str ( &req, client->rankexpr );
send_int ( &req, client->sort );
send_str ( &req, client->sortby );
send_str ( &req, query );
send_int ( &req, client->num_weights );
for ( i=0; i<client->num_weights; i++ )
send_int ( &req, client->weights[i] );
send_str ( &req, index_list );
send_int ( &req, 1 ); // id range bits
send_qword ( &req, client->minid );
send_qword ( &req, client->maxid );
send_int ( &req, client->num_filters );
for ( i=0; i<client->num_filters; i++ )
{
send_str ( &req, client->filters[i].attr );
send_int ( &req, client->filters[i].filter_type );
switch ( client->filters[i].filter_type )
{
case SPH_FILTER_VALUES:
send_int ( &req, client->filters[i].num_values );
if ( client->ver_search>=0x114 )
{
for ( j=0; j<client->filters[i].num_values; j++ )
send_qword ( &req, client->filters[i].values[j] );
} else
{
for ( j=0; j<client->filters[i].num_values; j++ )
send_int ( &req, (unsigned int)client->filters[i].values[j] );
}
break;
case SPH_FILTER_RANGE:
if ( client->ver_search>=0x114 )
{
send_qword ( &req, client->filters[i].umin );
send_qword ( &req, client->filters[i].umax );
} else
{
send_int ( &req, (unsigned int)client->filters[i].umin );
send_int ( &req, (unsigned int)client->filters[i].umax );
}
break;
case SPH_FILTER_FLOATRANGE:
send_float ( &req, client->filters[i].fmin );
send_float ( &req, client->filters[i].fmax );
break;
case SPH_FILTER_STRING:
send_str ( &req, client->filters[i].svalue );
break;
}
send_int ( &req, client->filters[i].exclude );
}
send_int ( &req, client->group_func );
send_str ( &req, client->group_by );
send_int ( &req, client->max_matches );
send_str ( &req, client->group_sort );
send_int ( &req, client->cutoff );
send_int ( &req, client->retry_count );
send_int ( &req, client->retry_delay );
send_str ( &req, client->group_distinct );
if ( client->geoanchor_attr_lat && client->geoanchor_attr_long )
{
send_int ( &req, 1 );
send_str ( &req, client->geoanchor_attr_lat );
send_str ( &req, client->geoanchor_attr_long );
send_float ( &req, client->geoanchor_lat );
send_float ( &req, client->geoanchor_long );
} else
{
send_int ( &req, 0 );
}
send_int ( &req, client->num_index_weights );
for ( i=0; i<client->num_index_weights; i++ )
{
send_str ( &req, client->index_weights_names[i] );
send_int ( &req, client->index_weights_values[i] );
}
send_int ( &req, client->max_query_time );
send_int ( &req, client->num_field_weights );
for ( i=0; i<client->num_field_weights; i++ )
{
send_str ( &req, client->field_weights_names[i] );
send_int ( &req, client->field_weights_values[i] );
}
send_str ( &req, comment );
if ( client->ver_search>=0x115 )
{
send_int ( &req, client->num_overrides );
for ( i=0; i<client->num_overrides; i++ )
{
send_str ( &req, client->overrides[i].attr );
send_int ( &req, SPH_ATTR_INTEGER );
send_int ( &req, client->overrides[i].num_values );
for ( j=0; j<client->overrides[i].num_values; j++ )
{
send_qword ( &req, client->overrides[i].docids[j] );
send_int ( &req, client->overrides[i].uint_values[j] );
}
}
}
if ( client->ver_search>=0x116 )
send_str ( &req, client->select_list );
if ( client->ver_search>=0x11B && client->predicted_time>0 )
send_int ( &req, client->predicted_time );
if ( client->ver_search>=0x11D )
{
send_str ( &req, client->outer_orderby );
send_int ( &req, client->outer_offset );
send_int ( &req, client->outer_limit );
send_int ( &req, client->has_outer );
}
if ( !req )
{
set_error ( client, "internal error, failed to build request" );
free ( client->reqs [ --client->num_reqs ] );
return -1;
}
return client->num_reqs-1;
}
static const char * sock_error ()
{
#if _WIN32
static char sBuf [ 256 ];
int iErr;
iErr = WSAGetLastError ();
_snprintf ( sBuf, sizeof(sBuf), "WSA error %d", iErr );
return sBuf;
#else
return strerror ( errno );
#endif
}
static int sock_errno ()
{
#ifdef _WIN32
return WSAGetLastError ();
#else
return errno;
#endif
}
static int sock_set_nonblocking ( int sock )
{
#if _WIN32
u_long uMode = 1;
return ioctlsocket ( sock, FIONBIO, &uMode );
#else
return fcntl ( sock, F_SETFL, O_NONBLOCK );
#endif
}
static int sock_set_blocking ( int sock )
{
#if _WIN32
u_long uMode = 0;
return ioctlsocket ( sock, FIONBIO, &uMode );
#else
return fcntl ( sock, F_SETFL, 0 );
#endif
}
void sock_close ( int sock )
{
if ( sock<0 )
return;
#if _WIN32
closesocket ( sock );
#else
close ( sock );
#endif
}
// wrap FD_SET to prevent warnings on Windows
#if _WIN32
#pragma warning(disable:4127) // conditional expr is const
#pragma warning(disable:4389) // signed/unsigned mismatch
void SPH_FD_SET ( int fd, fd_set * fdset ) { FD_SET ( fd, fdset ); }
#pragma warning(default:4127) // conditional expr is const
#pragma warning(default:4389) // signed/unsigned mismatch
#else // !USE_WINDOWS
#define SPH_FD_SET FD_SET
#endif
static sphinx_bool net_write ( int fd, const char * bytes, int len, sphinx_client * client )
{
int res;
#if defined(_WIN32) || defined(SO_NOSIGPIPE) || !defined(MSG_NOSIGNAL)
res = send ( fd, bytes, len, 0 );
#else
res = send ( fd, bytes, len, MSG_NOSIGNAL );
#endif
if ( res<0 )
{
set_error ( client, "send() error: %s", sock_error() );
return SPH_FALSE;
}
if ( res!=len )
{
set_error ( client, "send() error: incomplete write (len=%d, sent=%d)", len, res );
return SPH_FALSE;
}
return SPH_TRUE;
}
static sphinx_bool net_read ( int fd, char * buf, int len, sphinx_client * client )
{
int res, err;
for ( ;; )
{
res = recv ( fd, buf, len, 0 );
if ( res<0 )
{
err = sock_errno();
if ( err==EINTR || err==EWOULDBLOCK ) // FIXME! remove non-blocking mode here; add timeout
continue;
set_error ( client, "recv(): read error (error=%s)", sock_error() );
return SPH_FALSE;
}
len -= res;
buf += res;
if ( len==0 )
return SPH_TRUE;
if ( res==0 )
{
set_error ( client, "recv(): incomplete read (len=%d, recv=%d)", len, res );
return SPH_FALSE;
}
}
}
static int net_create_inet_sock ( sphinx_client * client )
{
struct hostent * hp;
struct sockaddr_in sa;
int sock, res, err, optval;
hp = gethostbyname ( client->host );
if ( !hp )
{
set_error ( client, "host name lookup failed (host=%s, error=%s)", client->host, sock_error() );
return -1;
}
memset ( &sa, 0, sizeof(sa) );
memcpy ( &sa.sin_addr, hp->h_addr_list[0], hp->h_length );
sa.sin_family = hp->h_addrtype;
sa.sin_port = htons ( (unsigned short)client->port );
sock = (int) socket ( hp->h_addrtype, SOCK_STREAM, 0 );
if ( sock<0 )
{
set_error ( client, "socket() failed: %s", sock_error() );
return -1;
}
if ( sock_set_nonblocking ( sock )<0 )
{
set_error ( client, "sock_set_nonblocking() failed: %s", sock_error() );
return -1;
}
optval = 1;
#if defined(SO_NOSIGPIPE)
if ( setsockopt ( sock, SOL_SOCKET, SO_NOSIGPIPE, (void *)&optval, (socklen_t)sizeof(optval) ) < 0 )
{
set_error ( client, "setsockopt() failed: %s", sock_error() );
return -1;
}
#endif
res = connect ( sock, (struct sockaddr*)&sa, sizeof(sa) );
if ( res==0 )
return sock;
err = sock_errno();
#ifdef EINPROGRESS
if ( err!=EWOULDBLOCK && err!=EINPROGRESS )
#else
if ( err!=EWOULDBLOCK )
#endif
{
set_error ( client, "connect() failed: %s", sock_error() );
return -1;
}
return sock;
}
#ifndef _WIN32
static int net_create_unix_sock ( sphinx_client * client )
{
struct hostent * hp;
struct sockaddr_un uaddr;
int sock, res, err, optval, len;
len = strlen ( client->host );
if ( len + 1 > sizeof( uaddr.sun_path ) )
set_error ( client, "UNIX socket path is too long (len=%d)", len );
memset ( &uaddr, 0, sizeof(uaddr) );
uaddr.sun_family = AF_UNIX;
memcpy ( uaddr.sun_path, client->host, len + 1 );
sock = socket ( AF_UNIX, SOCK_STREAM, 0 );
if ( sock<0 )
{
set_error ( client, "UNIX socket() failed: %s", sock_error() );
return -1;
}
if ( sock_set_nonblocking ( sock )<0 )
{
set_error ( client, "sock_set_nonblocking() failed: %s", sock_error() );
return -1;
}
optval = 1;
#if defined(SO_NOSIGPIPE)
if ( setsockopt ( sock, SOL_SOCKET, SO_NOSIGPIPE, (void *)&optval, (socklen_t)sizeof(optval) ) < 0 )
{
set_error ( client, "setsockopt() failed: %s", sock_error() );
return -1;
}
#endif
res = connect ( sock, (struct sockaddr *)&uaddr, sizeof(uaddr) );
if ( res==0 )
return sock;
err = sock_errno();
#ifdef EINPROGRESS
if ( err!=EWOULDBLOCK && err!=EINPROGRESS )
#else
if ( err!=EWOULDBLOCK )
#endif
{
set_error ( client, "connect() failed: %s", sock_error() );
return -1;
}
return sock;
}
#endif
static int net_connect_get ( sphinx_client * client )
{
struct timeval timeout;
fd_set fds_write;
int sock, to_wait, res, my_proto;
if ( client->sock>=0 )
return client->sock;
sock = -1;
if ( client->host[0]!='/' )
{
sock = net_create_inet_sock ( client );
} else
{
#ifdef _WIN32
set_error ( client, "UNIX sockets are not supported on Windows" );
return -1;
#else
sock = net_create_unix_sock ( client );
#endif
}
if ( sock<0 )
return -1;
to_wait = (int)( 1000*client->timeout );
if ( to_wait<=0 )
to_wait = CONNECT_TIMEOUT_MSEC;
{
timeout.tv_sec = to_wait / 1000; // full seconds
timeout.tv_usec = ( to_wait % 1000 ) * 1000; // remainder is msec, so *1000 for usec
FD_ZERO ( &fds_write );
SPH_FD_SET ( sock, &fds_write );
res = select ( 1+sock, NULL, &fds_write, NULL, &timeout );
if ( res>=0 && FD_ISSET ( sock, &fds_write ) )
{
sock_set_blocking ( sock );
// now send major client protocol version
my_proto = htonl ( 1 );
if ( !net_write ( sock, (char*)&my_proto, sizeof(my_proto), client ) )
{
sock_close ( sock );
set_error ( client, "failed to send client protocol version" );
return -1;
}
// check daemon version
if ( !net_read ( sock, (char*)&my_proto, sizeof(my_proto), client ) )
{
sock_close ( sock );
return -1;
}
my_proto = ntohl ( my_proto );
if ( my_proto<1 )
{
sock_close ( sock );
set_error ( client, "expected searchd protocol version 1+, got version %d", my_proto );
return -1;
}
return sock;
}
/*!COMMIT handle EINTR here*/
sock_close ( sock );
set_error ( client, "connect() timed out" );
return -1;
}
}
static sphinx_bool net_sock_eof ( int sock )
{
struct timeval tv;
fd_set fds_read, fds_except;
int res;
char buf;
// wrong arg, consider dead
if ( sock<0 )
return SPH_TRUE;
// select() on a socket and watch for exceptions
FD_ZERO ( &fds_read );
FD_ZERO ( &fds_except );
SPH_FD_SET ( sock, &fds_read );
SPH_FD_SET ( sock, &fds_except );
tv.tv_sec = 0;
tv.tv_usec = 0;
res = select ( 1+sock, &fds_read, NULL, &fds_except, &tv );
// select() failed, assume something is wrong
if ( res<0 )
return SPH_TRUE;
// got any events to read? (either normal via fds_read, or OOB via fds_except set)
if ( FD_ISSET ( sock, &fds_read ) || FD_ISSET ( sock, &fds_except ) )
if ( recv ( sock, &buf, sizeof(buf), MSG_PEEK )<=0 )
if ( sock_errno()!=EWOULDBLOCK )
return SPH_TRUE;
// it seems alive
return SPH_FALSE;
}
static int net_connect_ex ( sphinx_client * client )
{
if ( client->sock>=0 )
{
// in case of a persistent connection, check for eof
// then attempt to reestablish lost pconn once
if ( !net_sock_eof ( client->sock ) )
return client->sock;
sock_close ( client->sock );
client->sock = -1;
}
if ( !client->persist )
return net_connect_get ( client );
sphinx_open ( client );
return client->sock;
}
static unsigned short unpack_short ( char ** cur )
{
unsigned short v;
memcpy ( &v, *cur, sizeof(unsigned short) );
(*cur) += sizeof(unsigned short);
return ntohs ( v );
}
static unsigned int unpack_int ( char ** cur )
{
unsigned int v;
memcpy ( &v, *cur, sizeof(unsigned int) );
(*cur) += sizeof(unsigned int);
return ntohl ( v );
}
static char * unpack_str ( char ** cur )
{
// we play a trick
// we move the string in-place to free space for trailing zero but avoid malloc
unsigned int len;
len = unpack_int ( cur );
memmove ( (*cur)-1, (*cur), len );
(*cur) += len;
(*cur)[-1] = '\0';
return (*cur)-len-1;
}
static sphinx_uint64_t unpack_qword ( char ** cur )
{
sphinx_uint64_t hi, lo;
hi = unpack_int ( cur );
lo = unpack_int ( cur );
return ( hi<<32 ) + lo;
}
static float unpack_float ( char ** cur )
{
union
{
unsigned int n;
float f;
} u;
u.n = unpack_int ( cur );
return u.f;
}
static void net_get_response ( int fd, sphinx_client * client )
{
int len;
char header_buf[32], *cur, *response;
unsigned short status, ver;
// dismiss previous response
if ( client->response_buf )
{
free ( client->response_buf );
client->response_len = 0;
client->response_buf = NULL;
}
// read and parse the header
if ( !net_read ( fd, header_buf, 8, client ) )
{
sock_close ( fd );
if ( client->sock>0 )
client->sock = -1;
return;
}
cur = header_buf;
status = unpack_short ( &cur );
ver = unpack_short ( &cur );
len = unpack_int ( &cur );
// sanity check the length, alloc the buffer
if ( len<0 || len>MAX_PACKET_LEN )
{
sock_close ( fd );
if ( client->sock>0 )
client->sock = -1;
set_error ( client, "response length out of bounds (len=%d)", len );
return;
}
response = malloc ( len );
if ( !response )
{
sock_close ( fd );
if ( client->sock>0 )
client->sock = -1;
set_error ( client, "malloc() failed (bytes=%d)", len );
return;
}
// read the response
if ( !net_read ( fd, response, len, client ) )
{
sock_close ( fd );
if ( client->sock>0 )
client->sock = -1;
free ( response );
return;
}
// check status
cur = response;
switch ( status )
{
case SEARCHD_OK:
case SEARCHD_WARNING:
client->error = NULL; // so far so good
if ( status==SEARCHD_WARNING )
client->warning = unpack_str ( &cur );
else
client->warning = NULL;
client->response_len = len;
client->response_buf = response;
client->response_start = cur;
break;
case SEARCHD_ERROR:
case SEARCHD_RETRY:
// copy error message, so that we can immediately free the response
set_error ( client, "%s", unpack_str ( &cur ) );
free ( response );
break;
default:
set_error ( client, "unknown status code (status=%d)", status );
free ( response );
break;
}
// close one-time socket on success
if ( client->sock<0 )
sock_close ( fd );
}
sphinx_bool sphinx_open ( sphinx_client * client )
{
char buf[16], *pbuf;
if ( client->sock>=0 )
{
set_error ( client, "already connected" );
return SPH_FALSE;
}
client->sock = net_connect_get ( client );
if ( client->sock<0 )
return SPH_FALSE;
pbuf = buf;
send_word ( &pbuf, SEARCHD_COMMAND_PERSIST );
send_word ( &pbuf, 0 ); // dummy version
send_int ( &pbuf, 4 ); // dummy body len
send_int ( &pbuf, 1 ); // dummy body
if ( !net_write ( client->sock, buf, (int)(pbuf-buf), client ) )
{
sock_close ( client->sock );
client->sock = -1;
return SPH_FALSE;
}
client->persist = SPH_TRUE;
return SPH_TRUE;
}
sphinx_bool sphinx_close ( sphinx_client * client )
{
if ( client->sock<0 )
{
set_error ( client, "not connected" );
return SPH_FALSE;
}
sock_close ( client->sock );
client->sock = -1;
client->persist = SPH_FALSE;
return SPH_TRUE;
}
static void * sphinx_malloc ( int len, sphinx_client * client )
{
void * res;
if ( len<0 || len>MAX_PACKET_LEN )
{
set_error ( client, "malloc() length out of bounds, possibly broken response packet (len=%d)", len );
return NULL;
}
res = malloc ( len );
if ( !res )
set_error ( client, "malloc() failed (bytes=%d)", len );
return res;
}
sphinx_result * sphinx_run_queries ( sphinx_client * client )
{
int i, j, k, l, fd, len, nreqs, id64;
char req_header[32], *req, *p, *pmax;
sphinx_result * res;
union un_attr_value * pval;
if ( !client )
return NULL;
if ( client->num_reqs<=0 || client->num_reqs>MAX_REQS )
{
set_error ( client, "num_reqs=%d out of bounds (too many queries?)", client->num_reqs );
return NULL;
}
fd = net_connect_ex ( client );
if ( fd<0 )
return NULL;
// free previous results
sphinx_free_results ( client );
// send query, get response
len = 8;
for ( i=0; i<client->num_reqs; i++ )
len += client->req_lens[i];
req = req_header;
send_word ( &req, SEARCHD_COMMAND_SEARCH );
send_word ( &req, client->ver_search );
send_int ( &req, len );
send_int ( &req, 0 ); // its a client
send_int ( &req, client->num_reqs );
if ( !net_write ( fd, req_header, (int)(req-req_header), client ) )
return NULL;
for ( i=0; i<client->num_reqs; i++ )
if ( !net_write ( fd, client->reqs[i], client->req_lens[i], client ) )
return NULL;
net_get_response ( fd, client );
if ( !client->response_buf )
return NULL;
// dismiss request data, memorize count
nreqs = sphinx_dismiss_requests ( client );
// parse response
p = client->response_start;
pmax = client->response_start + client->response_len; // max position for checks, to protect against broken responses
for ( i=0; i<nreqs && p<pmax; i++ )
{
res = &client->results[i];
client->num_results++;
res->error = NULL;
res->warning = NULL;
res->status = unpack_int ( &p );
if ( res->status!=SEARCHD_OK )
{
if ( res->status==SEARCHD_WARNING )
{
res->warning = unpack_str ( &p );
} else
{
res->error = unpack_str ( &p );
continue;
}
}
// fields
res->num_fields = unpack_int ( &p );
res->fields = sphinx_malloc ( res->num_fields*sizeof(const char*), client );
if ( !res->fields )
return NULL;
for ( j=0; j<res->num_fields; j++ )
res->fields[j] = unpack_str ( &p );
// attrs
res->num_attrs = unpack_int ( &p );
res->attr_names = sphinx_malloc ( res->num_attrs*sizeof(const char*), client );
if ( !res->attr_names )
return NULL;
res->attr_types = sphinx_malloc ( res->num_attrs*sizeof(int), client );
if ( !res->attr_types )
return NULL;
for ( j=0; j<res->num_attrs; j++ )
{
res->attr_names[j] = unpack_str ( &p );
res->attr_types[j] = unpack_int ( &p );
}
// match count, id bits flag
res->num_matches = unpack_int ( &p );
id64 = unpack_int ( &p );
res->values_pool = sphinx_malloc ( (2+res->num_attrs) * res->num_matches * sizeof(union un_attr_value), client );
if ( !res->values_pool )
return NULL;
pval = res->values_pool;
// matches
for ( j=0; j<res->num_matches; j++ )
{
// id
if ( id64 )
pval->int_value = unpack_qword ( &p );
else
pval->int_value = unpack_int ( &p );
pval++;
// weight
pval->int_value = unpack_int ( &p );
pval++;
// attrs
for ( k=0; k<res->num_attrs; k++ )
{
switch ( res->attr_types[k] )
{
case SPH_ATTR_MULTI64:
case SPH_ATTR_MULTI:
/*!COMMIT this is totally unsafe on some arches (eg. SPARC)*/
pval->mva_value = (unsigned int *) p;
len = unpack_int ( &p );
for ( l=0; l<=len; l++ ) // including first one that is len
pval->mva_value[l] = ntohl ( pval->mva_value[l] );
if ( res->attr_types[k]==SPH_ATTR_MULTI64 )
{
pval->mva_value[0] = pval->mva_value[0]/2;
}
p += len*sizeof(unsigned int);
break;
case SPH_ATTR_FLOAT: pval->float_value = unpack_float ( &p ); break;
case SPH_ATTR_BIGINT: pval->int_value = unpack_qword ( &p ); break;
case SPH_ATTR_STRING: pval->string = unpack_str ( &p ); break;
case SPH_ATTR_FACTORS:
len = unpack_int ( &p );
if ( len )
p += len-sizeof(unsigned int);
break;
default: pval->int_value = unpack_int ( &p ); break;
}
pval++;
}
}
// totals
res->total = unpack_int ( &p );
res->total_found = unpack_int ( &p );
res->time_msec = unpack_int ( &p );
res->num_words = unpack_int ( &p );
if ( res->words )
free ( res->words );
res->words = sphinx_malloc ( res->num_words*sizeof(struct st_sphinx_wordinfo), client );
if ( !res->words )
return NULL;
// words
for ( j=0; j<res->num_words; j++ )
{
res->words[j].word = unpack_str ( &p );
res->words[j].docs = unpack_int ( &p );
res->words[j].hits = unpack_int ( &p );
}
// sanity check
// FIXME! add it to each unpack?
if ( p>pmax )
{
set_error ( client, "unpack error (req=%d, reqs=%d)", i, nreqs );
return NULL;
}
}
return client->results;
}
//////////////////////////////////////////////////////////////////////////
int sphinx_get_num_results ( sphinx_client * client )
{
return client ? client->num_results : -1;
}
sphinx_uint64_t sphinx_get_id ( sphinx_result * result, int match )
{
return sphinx_get_int ( result, match, -2 );
}
int sphinx_get_weight ( sphinx_result * result, int match )
{
return (int)sphinx_get_int ( result, match, -1 );
}
sphinx_int64_t sphinx_get_int ( sphinx_result * result, int match, int attr )
{
// FIXME! add safety and type checks
union un_attr_value * pval;
pval = result->values_pool;
return pval [ (2+result->num_attrs)*match+2+attr ].int_value;
}
float sphinx_get_float ( sphinx_result * result, int match, int attr )
{
// FIXME! add safety and type checks
union un_attr_value * pval;
pval = result->values_pool;
return pval [ (2+result->num_attrs)*match+2+attr ].float_value;
}
unsigned int * sphinx_get_mva ( sphinx_result * result, int match, int attr )
{
// FIXME! add safety and type checks
union un_attr_value * pval;
pval = result->values_pool;
return pval [ (2+result->num_attrs)*match+2+attr ].mva_value;
}
sphinx_uint64_t sphinx_get_mva64_value ( unsigned int * mva, int i )
{
sphinx_uint64_t uVal;
uVal = ( ( ( (sphinx_uint64_t)( mva[i*2] ) )<<32 ) | (sphinx_uint64_t)( mva[i*2+1] ) );
return uVal;
}
const char * sphinx_get_string ( sphinx_result * result, int match, int attr )
{
// FIXME! add safety and type checks
union un_attr_value * pval;
pval = result->values_pool;
return pval [ (2+result->num_attrs)*match+2+attr ].string;
}
//////////////////////////////////////////////////////////////////////////
static sphinx_bool net_simple_query ( sphinx_client * client, char * buf, int req_len )
{
int fd;
fd = net_connect_ex ( client );
if ( fd<0 )
{
free ( buf );
return SPH_FALSE;
}
if ( !net_write ( fd, buf, 8+req_len, client ) )
{
free ( buf );
return SPH_FALSE;
}
free ( buf );
net_get_response ( fd, client );
if ( !client->response_buf )
return SPH_FALSE;
return SPH_TRUE;
}
void sphinx_init_excerpt_options ( sphinx_excerpt_options * opts )
{
if ( !opts )
return;
opts->before_match = "<b>";
opts->after_match = "</b>";
opts->chunk_separator = " ... ";
opts->html_strip_mode = "index";
opts->passage_boundary = "none";
opts->limit = 256;
opts->limit_passages = 0;
opts->limit_words = 0;
opts->around = 5;
opts->start_passage_id = 1;
opts->exact_phrase = SPH_FALSE;
opts->single_passage = SPH_FALSE;
opts->use_boundaries = SPH_FALSE;
opts->weight_order = SPH_FALSE;
opts->query_mode = SPH_FALSE;
opts->force_all_words = SPH_FALSE;
opts->load_files = SPH_FALSE;
opts->allow_empty = SPH_FALSE;
opts->emit_zones = SPH_FALSE;
}
char ** sphinx_build_excerpts ( sphinx_client * client, int num_docs, const char ** docs, const char * index, const char * words, sphinx_excerpt_options * opts )
{
sphinx_excerpt_options def_opt;
int i, req_len, flags;
char *buf, *req, *p, *pmax, **result;
if ( !client || !docs || !index || !words || num_docs<=0 )
{
if ( !docs ) set_error ( client, "invalid arguments (docs must not be empty)" );
else if ( !index ) set_error ( client, "invalid arguments (index must not be empty)" );
else if ( !words ) set_error ( client, "invalid arguments (words must not be empty)" );
else if ( num_docs<=0 ) set_error ( client, "invalid arguments (num_docs must be positive)" );
return NULL;
}
// fixup options
if ( !opts )
{
sphinx_init_excerpt_options ( &def_opt );
opts = &def_opt;
}
// alloc buffer
req_len = (int)( 60
+ strlen(index)
+ strlen(words)
+ safestrlen(opts->before_match)
+ safestrlen(opts->after_match)
+ safestrlen(opts->chunk_separator)
+ safestrlen(opts->html_strip_mode)
+ safestrlen(opts->passage_boundary) );
for ( i=0; i<num_docs; i++ )
req_len += (int)( 4 + safestrlen(docs[i]) );
buf = malloc ( 12+req_len ); // request body length plus 12 header bytes
if ( !buf )
{
set_error ( client, "malloc() failed (bytes=%d)", req_len );
return NULL;
}
// build request
req = buf;
send_word ( &req, SEARCHD_COMMAND_EXCERPT );
send_word ( &req, VER_COMMAND_EXCERPT );
send_int ( &req, req_len );
flags = 1; // remove spaces
if ( opts->exact_phrase ) flags |= 2;
if ( opts->single_passage ) flags |= 4;
if ( opts->use_boundaries ) flags |= 8;
if ( opts->weight_order ) flags |= 16;
if ( opts->query_mode ) flags |= 32;
if ( opts->force_all_words ) flags |= 64;
if ( opts->load_files ) flags |= 128;
if ( opts->allow_empty ) flags |= 256;
if ( opts->emit_zones ) flags |= 512;
send_int ( &req, 0 );
send_int ( &req, flags );
send_str ( &req, index );
send_str ( &req, words );
send_str ( &req, opts->before_match );
send_str ( &req, opts->after_match );
send_str ( &req, opts->chunk_separator );
send_int ( &req, opts->limit );
send_int ( &req, opts->around );
send_int ( &req, opts->limit_passages ); // v1.2
send_int ( &req, opts->limit_words );
send_int ( &req, opts->start_passage_id );
send_str ( &req, opts->html_strip_mode );
send_str ( &req, opts->passage_boundary );
send_int ( &req, num_docs );
for ( i=0; i<num_docs; i++ )
send_str ( &req, docs[i] );
if ( (int)(req-buf)!=8+req_len )
{
set_error ( client, "internal error: failed to build request in sphinx_build_excerpts()" );
free ( buf );
return NULL;
}
// send query, get response
if ( !net_simple_query ( client, buf, req_len ) )
return NULL;
// parse response
p = client->response_start;
pmax = client->response_start + client->response_len; // max position for checks, to protect against broken responses
result = malloc ( (1+num_docs)*sizeof(char*) );
if ( !result )
{
set_error ( client, "malloc() failed (bytes=%d)", (1+num_docs)*sizeof(char*) );
return NULL;
}
for ( i=0; i<=num_docs; i++ )
result[i] = NULL;
for ( i=0; i<num_docs && p<pmax; i++ )
result[i] = strdup ( unpack_str ( &p ) );
if ( p>pmax )
{
for ( i=0; i<num_docs; i++ )
if ( result[i] )
free ( result[i] );
set_error ( client, "unpack error" );
return NULL;
}
// done
return result;
}
//////////////////////////////////////////////////////////////////////////
int sphinx_update_attributes ( sphinx_client * client, const char * index, int num_attrs, const char ** attrs, int num_docs, const sphinx_uint64_t * docids, const sphinx_int64_t * values )
{
int i, j, req_len;
char *buf, *req, *p;
// check args
if ( !client || num_attrs<=0 || !attrs || num_docs<=0 || !docids || !values )
{
if ( num_attrs<=0 ) set_error ( client, "invalid arguments (num_attrs must be positive)" );
else if ( !index ) set_error ( client, "invalid arguments (index must not be empty)" );
else if ( !attrs ) set_error ( client, "invalid arguments (attrs must not empty)" );
else if ( num_docs<=0 ) set_error ( client, "invalid arguments (num_docs must be positive)" );
else if ( !docids ) set_error ( client, "invalid arguments (docids must not be empty)" );
else if ( !values ) set_error ( client, "invalid arguments (values must not be empty)" );
}
// alloc buffer
req_len = (int)( 12 + safestrlen(index) + (12+4*num_attrs)*num_docs );
for ( i=0; i<num_attrs; i++ )
req_len += (int)( 4 + safestrlen(attrs[i]) );
buf = malloc ( 12+req_len ); // request body length plus 12 header bytes
if ( !buf )
{
set_error ( client, "malloc() failed (bytes=%d)", req_len );
return -1;
}
// build request
req = buf;
send_word ( &req, SEARCHD_COMMAND_UPDATE );
send_word ( &req, VER_COMMAND_UPDATE );
send_int ( &req, req_len );
send_str ( &req, index );
send_int ( &req, num_attrs );
for ( i=0; i<num_attrs; i++ )
{
send_str ( &req, attrs[i] );
send_int ( &req, 0 ); // not SPH_ATTR_MULTI flag
}
send_int ( &req, num_docs );
for ( i=0; i<num_docs; i++ )
{
send_qword ( &req, docids[i] );
for ( j=0; j<num_attrs; j++ )
send_int ( &req, (unsigned int)( *values++ ) );
}
// send query, get response
if ( !net_simple_query ( client, buf, req_len ) )
return -1;
// parse response
if ( client->response_len<4 )
{
set_error ( client, "incomplete reply" );
return -1;
}
p = client->response_start;
return unpack_int ( &p );
}
int sphinx_update_attributes_mva ( sphinx_client * client, const char * index, const char * attr, sphinx_uint64_t docid, int num_values, const unsigned int * values )
{
int i, req_len;
char *buf, *req, *p;
// check args
if ( !client || !index || !attr || num_values<=0 || !values )
{
if ( !index ) set_error ( client, "invalid arguments (index must not be empty)" );
else if ( !attr ) set_error ( client, "invalid arguments (attr must not empty)" );
else if ( num_values<=0 ) set_error ( client, "invalid arguments (num_values must be positive)" );
else if ( !values ) set_error ( client, "invalid arguments (values must not be empty)" );
}
// alloc buffer
req_len = (int)( 38 + safestrlen(index) + safestrlen(attr) + num_values*4 );
buf = malloc ( 12+req_len ); // request body length plus 12 header bytes
if ( !buf )
{
set_error ( client, "malloc() failed (bytes=%d)", req_len );
return -1;
}
// build request
req = buf;
send_word ( &req, SEARCHD_COMMAND_UPDATE );
send_word ( &req, VER_COMMAND_UPDATE );
send_int ( &req, req_len );
send_str ( &req, index );
send_int ( &req, 1 );
send_str ( &req, attr );
send_int ( &req, 1 ); // SPH_ATTR_MULTI flag
send_int ( &req, 1 );
send_qword ( &req, docid );
send_int ( &req, num_values );
for ( i=0; i<num_values; i++ )
send_int ( &req, values[i] );
// send query, get response
if ( !net_simple_query ( client, buf, req_len ) )
return -1;
// parse response
if ( client->response_len<4 )
{
set_error ( client, "incomplete reply" );
return -1;
}
p = client->response_start;
return unpack_int ( &p );
}
//////////////////////////////////////////////////////////////////////////
// add by dengsl 20140711
int sphinx_flush_attributes(sphinx_client * client) {
char *buf, *req, *p;
int req_len = 0;
if (!client) {
printf("not valid client\n");
return -1;
}
buf = malloc ( 12 + req_len ); // request body length plus 12 header bytes
if ( !buf )
{
set_error ( client, "malloc() failed (bytes=%d)", req_len );
return -1;
}
req = buf;
send_word ( &req, SEARCHD_COMMAND_FLUSHATTRS );
send_word ( &req, VER_COMMAND_FLUSHATTRS );
send_int ( &req, req_len );
// send query, get response
if ( !net_simple_query ( client, buf, req_len ) )
return -1;
// parse response
if ( client->response_len < 4 )
{
set_error ( client, "incomplete reply" );
return -1;
}
p = client->response_start;
return unpack_int ( &p );
}
//add end
sphinx_keyword_info * sphinx_build_keywords ( sphinx_client * client, const char * query, const char * index, sphinx_bool hits, int * out_num_keywords )
{
int i, req_len, nwords, len;
char *buf, *req, *p, *pmax;
sphinx_keyword_info *res;
// check args
if ( !client || !query || !index )
{
if ( !query ) set_error ( client, "invalid arguments (query must not be empty)" );
else if ( !index ) set_error ( client, "invalid arguments (index must not be empty)" );
else if ( !out_num_keywords ) set_error ( client, "invalid arguments (out_num_keywords must not be null)" );
return NULL;
}
// alloc buffer
req_len = (int)( safestrlen(query) + safestrlen(index) + 12 );
buf = malloc ( 12+req_len ); // request body length plus 12 header bytes
if ( !buf )
{
set_error ( client, "malloc() failed (bytes=%d)", req_len );
return NULL;
}
// build request
req = buf;
send_word ( &req, SEARCHD_COMMAND_KEYWORDS );
send_word ( &req, VER_COMMAND_KEYWORDS );
send_int ( &req, req_len );
send_str ( &req, query );
send_str ( &req, index );
send_int ( &req, hits );
// send query, get response
if ( !net_simple_query ( client, buf, req_len ) )
return NULL;
// parse response
p = client->response_start;
pmax = client->response_start + client->response_len; // max position for checks, to protect against broken responses
nwords = unpack_int ( &p );
*out_num_keywords = nwords;
len = nwords*sizeof(sphinx_keyword_info);
res = (sphinx_keyword_info*) malloc ( len );
if ( !res )
{
set_error ( client, "malloc() failed (bytes=%d)", len );
return NULL;
}
memset ( res, 0, len );
for ( i=0; i<nwords && p<pmax; i++ )
{
res[i].tokenized = strdup ( unpack_str ( &p ) );
res[i].normalized = strdup ( unpack_str ( &p ) );
if ( hits )
{
res[i].num_docs = unpack_int ( &p );
res[i].num_hits = unpack_int ( &p );
}
}
// FIXME! add check for incomplete reply
return res;
}
//////////////////////////////////////////////////////////////////////////
char ** sphinx_status ( sphinx_client * client, int * num_rows, int * num_cols )
{
return sphinx_status_extended ( client, num_rows, num_cols, 0 );
}
char ** sphinx_status_extended ( sphinx_client * client, int * num_rows, int * num_cols, int local )
{
int i, j, k, n;
char *p, *pmax, *req, *buf, **res;
// check args
if ( !client || !num_rows || !num_cols )
{
if ( !num_rows ) set_error ( client, "invalid arguments (num_rows must not be NULL)" );
else if ( !num_cols ) set_error ( client, "invalid arguments (num_cols must not be NULL)" );
return NULL;
}
// build request
buf = malloc ( 12 );
if ( !buf )
{
set_error ( client, "malloc() failed (bytes=12)" );
return NULL;
}
if (local)
local=0;
else
local=1;
req = buf;
send_word ( &req, SEARCHD_COMMAND_STATUS );
send_word ( &req, VER_COMMAND_STATUS );
send_int ( &req, 4 );
send_int ( &req, local );
// send query, get response
if ( !net_simple_query ( client, buf, 12 ) )
return NULL;
// parse response
p = client->response_start;
pmax = client->response_start + client->response_len; // max position for checks, to protect against broken responses
*num_rows = unpack_int ( &p );
*num_cols = unpack_int ( &p );
n = (*num_rows)*(*num_cols);
res = (char**) malloc ( n*sizeof(char*) );
for ( i=0; i<n; i++ )
res[i] = NULL;
// FIXME! error checking?
k = 0;
for ( i=0; i<*num_rows; i++ )
for ( j=0; j<*num_cols; j++ )
res[k++] = strdup ( unpack_str ( &p ) );
return res;
}
void sphinx_status_destroy ( char ** status, int num_rows, int num_cols )
{
int i;
for ( i=0; i<num_rows*num_cols; i++ )
free ( status[i] );
free ( status );
}
//
// $Id: sphinxclient.c 4097 2013-08-20 09:28:24Z kevg $
//
//
// $Id: sphinxclient.h 3883 2013-05-24 07:15:19Z tomat $
//
//
// Copyright (c) 2001-2013, Andrew Aksyonoff
// Copyright (c) 2008-2013, Sphinx Technologies Inc
// All rights reserved
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU Library General Public License. You should
// have received a copy of the LGPL license along with this program; if you
// did not, you can find it at http://www.gnu.org/
//
#ifndef _sphinxclient_
#define _sphinxclient_
#ifdef __cplusplus
extern "C" {
#endif
/// known searchd status codes
enum
{
SEARCHD_OK = 0,
SEARCHD_ERROR = 1,
SEARCHD_RETRY = 2,
SEARCHD_WARNING = 3
};
/// known match modes
enum
{
SPH_MATCH_ALL = 0,
SPH_MATCH_ANY = 1,
SPH_MATCH_PHRASE = 2,
SPH_MATCH_BOOLEAN = 3,
SPH_MATCH_EXTENDED = 4,
SPH_MATCH_FULLSCAN = 5,
SPH_MATCH_EXTENDED2 = 6
};
/// known ranking modes (ext2 only)
enum
{
SPH_RANK_PROXIMITY_BM25 = 0,
SPH_RANK_BM25 = 1,
SPH_RANK_NONE = 2,
SPH_RANK_WORDCOUNT = 3,
SPH_RANK_PROXIMITY = 4,
SPH_RANK_MATCHANY = 5,
SPH_RANK_FIELDMASK = 6,
SPH_RANK_SPH04 = 7,
SPH_RANK_EXPR = 8,
SPH_RANK_TOTAL = 9,
SPH_RANK_DEFAULT = SPH_RANK_PROXIMITY_BM25
};
/// known sort modes
enum
{
SPH_SORT_RELEVANCE = 0,
SPH_SORT_ATTR_DESC = 1,
SPH_SORT_ATTR_ASC = 2,
SPH_SORT_TIME_SEGMENTS = 3,
SPH_SORT_EXTENDED = 4,
SPH_SORT_EXPR = 5
};
/// known filter types
enum
{ SPH_FILTER_VALUES = 0,
SPH_FILTER_RANGE = 1,
SPH_FILTER_FLOATRANGE = 2,
SPH_FILTER_STRING = 3
};
/// known attribute types
enum
{
SPH_ATTR_INTEGER = 1,
SPH_ATTR_TIMESTAMP = 2,
SPH_ATTR_ORDINAL = 3,
SPH_ATTR_BOOL = 4,
SPH_ATTR_FLOAT = 5,
SPH_ATTR_BIGINT = 6,
SPH_ATTR_STRING = 7,
SPH_ATTR_FACTORS = 1001,
SPH_ATTR_MULTI = 0x40000001UL,
SPH_ATTR_MULTI64 = 0x40000002UL
};
/// known grouping functions
enum
{ SPH_GROUPBY_DAY = 0,
SPH_GROUPBY_WEEK = 1,
SPH_GROUPBY_MONTH = 2,
SPH_GROUPBY_YEAR = 3,
SPH_GROUPBY_ATTR = 4,
SPH_GROUPBY_ATTRPAIR = 5
};
//////////////////////////////////////////////////////////////////////////
#if defined(_MSC_VER)
typedef __int64 sphinx_int64_t;
typedef unsigned __int64 sphinx_uint64_t;
#else // !defined(_MSC_VER)
typedef long long sphinx_int64_t;
typedef unsigned long long sphinx_uint64_t;
#endif // !defined(_MSC_VER)
typedef int sphinx_bool;
#define SPH_TRUE 1
#define SPH_FALSE 0
//////////////////////////////////////////////////////////////////////////
typedef struct st_sphinx_client sphinx_client;
typedef struct st_sphinx_wordinfo
{
const char * word;
int docs;
int hits;
} sphinx_wordinfo;
typedef struct st_sphinx_result
{
const char * error;
const char * warning;
int status;
int num_fields;
char ** fields;
int num_attrs;
char ** attr_names;
int * attr_types;
int num_matches;
void * values_pool;
int total;
int total_found;
int time_msec;
int num_words;
sphinx_wordinfo * words;
} sphinx_result;
typedef struct st_sphinx_excerpt_options
{
const char * before_match;
const char * after_match;
const char * chunk_separator;
const char * html_strip_mode;
const char * passage_boundary;
int limit;
int limit_passages;
int limit_words;
int around;
int start_passage_id;
sphinx_bool exact_phrase;
sphinx_bool single_passage;
sphinx_bool use_boundaries;
sphinx_bool weight_order;
sphinx_bool query_mode;
sphinx_bool force_all_words;
sphinx_bool load_files;
sphinx_bool allow_empty;
sphinx_bool emit_zones;
} sphinx_excerpt_options;
typedef struct st_sphinx_keyword_info
{
char * tokenized;
char * normalized;
int num_docs;
int num_hits;
} sphinx_keyword_info;
//////////////////////////////////////////////////////////////////////////
sphinx_client * sphinx_create ( sphinx_bool copy_args );
void sphinx_cleanup ( sphinx_client * client );
void sphinx_destroy ( sphinx_client * client );
const char * sphinx_error ( sphinx_client * client );
const char * sphinx_warning ( sphinx_client * client );
sphinx_bool sphinx_set_server ( sphinx_client * client, const char * host, int port );
sphinx_bool sphinx_set_connect_timeout ( sphinx_client * client, float seconds );
sphinx_bool sphinx_open ( sphinx_client * client );
sphinx_bool sphinx_close ( sphinx_client * client );
sphinx_bool sphinx_set_limits ( sphinx_client * client, int offset, int limit, int max_matches, int cutoff );
sphinx_bool sphinx_set_max_query_time ( sphinx_client * client, int max_query_time );
sphinx_bool sphinx_set_match_mode ( sphinx_client * client, int mode );
sphinx_bool sphinx_set_ranking_mode ( sphinx_client * client, int ranker, const char * rankexpr );
sphinx_bool sphinx_set_sort_mode ( sphinx_client * client, int mode, const char * sortby );
sphinx_bool sphinx_set_field_weights ( sphinx_client * client, int num_weights, const char ** field_names, const int * field_weights );
sphinx_bool sphinx_set_index_weights ( sphinx_client * client, int num_weights, const char ** index_names, const int * index_weights );
sphinx_bool sphinx_set_id_range ( sphinx_client * client, sphinx_uint64_t minid, sphinx_uint64_t maxid );
sphinx_bool sphinx_add_filter ( sphinx_client * client, const char * attr, int num_values, const sphinx_int64_t * values, sphinx_bool exclude );
sphinx_bool sphinx_add_filter_string ( sphinx_client * client, const char * attr, const char * value, sphinx_bool exclude );
sphinx_bool sphinx_add_filter_range ( sphinx_client * client, const char * attr, sphinx_int64_t umin, sphinx_int64_t umax, sphinx_bool exclude );
sphinx_bool sphinx_add_filter_float_range ( sphinx_client * client, const char * attr, float fmin, float fmax, sphinx_bool exclude );
sphinx_bool sphinx_set_geoanchor ( sphinx_client * client, const char * attr_latitude, const char * attr_longitude, float latitude, float longitude );
sphinx_bool sphinx_set_groupby ( sphinx_client * client, const char * attr, int groupby_func, const char * group_sort );
sphinx_bool sphinx_set_groupby_distinct ( sphinx_client * client, const char * attr );
sphinx_bool sphinx_set_retries ( sphinx_client * client, int count, int delay );
sphinx_bool sphinx_add_override ( sphinx_client * client, const char * attr, const sphinx_uint64_t * docids, int num_values, const unsigned int * values );
sphinx_bool sphinx_set_select ( sphinx_client * client, const char * select_list );
sphinx_bool sphinx_set_query_flags ( sphinx_client * client, const char * flag_name, sphinx_bool enabled, int max_predicted_msec );
void sphinx_reset_query_flags ( sphinx_client * client );
sphinx_bool sphinx_set_outer_select ( sphinx_client * client, const char * orderby, int offset, int limit );
void sphinx_reset_outer_select ( sphinx_client * client );
void sphinx_reset_filters ( sphinx_client * client );
void sphinx_reset_groupby ( sphinx_client * client );
sphinx_result * sphinx_query ( sphinx_client * client, const char * query, const char * index_list, const char * comment );
int sphinx_add_query ( sphinx_client * client, const char * query, const char * index_list, const char * comment );
sphinx_result * sphinx_run_queries ( sphinx_client * client );
int sphinx_get_num_results ( sphinx_client * client );
sphinx_uint64_t sphinx_get_id ( sphinx_result * result, int match );
int sphinx_get_weight ( sphinx_result * result, int match );
sphinx_int64_t sphinx_get_int ( sphinx_result * result, int match, int attr );
float sphinx_get_float ( sphinx_result * result, int match, int attr );
unsigned int * sphinx_get_mva ( sphinx_result * result, int match, int attr );
sphinx_uint64_t sphinx_get_mva64_value ( unsigned int * mva, int i );
const char * sphinx_get_string ( sphinx_result * result, int match, int attr );
void sphinx_init_excerpt_options ( sphinx_excerpt_options * opts );
char ** sphinx_build_excerpts ( sphinx_client * client, int num_docs, const char ** docs, const char * index, const char * words, sphinx_excerpt_options * opts );
int sphinx_update_attributes ( sphinx_client * client, const char * index, int num_attrs, const char ** attrs, int num_docs, const sphinx_uint64_t * docids, const sphinx_int64_t * values );
int sphinx_update_attributes_mva ( sphinx_client * client, const char * index, const char * attr, sphinx_uint64_t docid, int num_values, const unsigned int * values );
int sphinx_flush_attributes(sphinx_client * client);
sphinx_keyword_info * sphinx_build_keywords ( sphinx_client * client, const char * query, const char * index, sphinx_bool hits, int * out_num_keywords );
char ** sphinx_status ( sphinx_client * client, int * num_rows, int * num_cols );
char ** sphinx_status_extended ( sphinx_client * client, int * num_rows, int * num_cols, int local );
void sphinx_status_destroy ( char ** status, int num_rows, int num_cols );
/////////////////////////////////////////////////////////////////////////////
#ifdef __cplusplus
}
#endif
#endif // _sphinxclient_
//
// $Id: sphinxclient.h 3883 2013-05-24 07:15:19Z tomat $
//
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment