//////////////////////////////////////////////////////////////////////////////// /// @brief fluent query /// /// @file /// /// DISCLAIMER /// /// Copyright 2010-2011 triagens GmbH, Cologne, Germany /// /// Licensed under the Apache License, Version 2.0 (the "License"); /// you may not use this file except in compliance with the License. /// You may obtain a copy of the License at /// /// http://www.apache.org/licenses/LICENSE-2.0 /// /// Unless required by applicable law or agreed to in writing, software /// distributed under the License is distributed on an "AS IS" BASIS, /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. /// See the License for the specific language governing permissions and /// limitations under the License. /// /// Copyright holder is triAGENS GmbH, Cologne, Germany /// /// @author Dr. Frank Celler /// @author Copyright 2011, triagens GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// #include "fluent-query.h" #include "BasicsC/conversions.h" #include "BasicsC/logging.h" #include "BasicsC/string-buffer.h" #include "BasicsC/strings.h" #include "ShapedJson/shape-accessor.h" #include "ShapedJson/shaped-json.h" #include "VocBase/index.h" #include "VocBase/simple-collection.h" #include "VocBase/voc-shaper.h" // ----------------------------------------------------------------------------- // --SECTION-- forward declarations // ----------------------------------------------------------------------------- static void ExecuteDocumentQueryPrimary (TRI_fluent_query_t* qry, TRI_fluent_query_result_t* result); // ----------------------------------------------------------------------------- // --SECTION-- private functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup VocBase /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief frees the old storage of a TRI_fluent_query_result_t /// /// Note the augument json objects are not freed. //////////////////////////////////////////////////////////////////////////////// static void FreeDocuments (TRI_fluent_query_result_t* result) { if (result->_documents != NULL) { TRI_Free(result->_documents); result->_documents = NULL; } if (result->_augmented != NULL) { TRI_Free(result->_augmented); result->_augmented = NULL; } } //////////////////////////////////////////////////////////////////////////////// /// @brief frees the augument json objects //////////////////////////////////////////////////////////////////////////////// static void FreeAugmented (TRI_fluent_query_result_t* result) { TRI_json_t* ptr; TRI_json_t* end; if (result->_augmented != NULL) { ptr = result->_augmented; end = result->_augmented + result->_length; for (; ptr < end; ++ptr) { TRI_DestroyJson(ptr); } result->_augmented = NULL; } } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- QUERY // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // --SECTION-- private functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup VocBase /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief clones a TRI_fluent_query_t //////////////////////////////////////////////////////////////////////////////// static void CloneQuery (TRI_fluent_query_t* dst, TRI_fluent_query_t* src) { dst->_type = src->_type; dst->_collection = src->_collection; dst->_isOptimised = src->_isOptimised; dst->optimise = src->optimise; dst->execute = src->execute; dst->json = src->json; dst->stringify = src->stringify; dst->clone = src->clone; dst->free = src->free; } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- COLLECTION QUERY // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // --SECTION-- private functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup VocBase /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief optimises a full scan //////////////////////////////////////////////////////////////////////////////// static char* OptimiseCollectionQuery (TRI_fluent_query_t** qry) { TRI_document_query_t* query; TRI_col_type_e type; // extract query query = (TRI_document_query_t*) *qry; // sanity check if (! query->base._collection->_loaded) { return TRI_Concatenate3String("collection \"", query->base._collection->_name, "\" not loaded"); } // check if query is already optimised if (query->base._isOptimised) { return NULL; } type = query->base._collection->_collection->base._type; if (type != TRI_COL_TYPE_SIMPLE_DOCUMENT) { return TRI_DuplicateString("cannot handle collection type"); } // nothing to optimise query->base._isOptimised = true; return NULL; } //////////////////////////////////////////////////////////////////////////////// /// @brief executes a full scan //////////////////////////////////////////////////////////////////////////////// static void ExecuteCollectionQuery (TRI_fluent_query_t* qry, TRI_fluent_query_result_t* result) { TRI_doc_mptr_t const** qtr; TRI_document_query_t* query; TRI_sim_collection_t* collection; size_t total; void** end; void** ptr; // extract query and document query = (TRI_document_query_t*) qry; collection = (TRI_sim_collection_t*) query->base._collection->_collection; // append information about the execution plan TRI_AppendString(&result->_cursor, ">collection"); // free any old storage FreeAugmented(result); FreeDocuments(result); // add a new document list and copy all documents result->_total = result->_length = 0; if (collection->_primaryIndex._nrUsed == 0) { return; } result->_documents = (qtr = TRI_Allocate(sizeof(TRI_doc_mptr_t*) * (collection->_primaryIndex._nrUsed))); ptr = collection->_primaryIndex._table; end = collection->_primaryIndex._table + collection->_primaryIndex._nrAlloc; for (total = 0; ptr < end; ++ptr) { ++result->_scannedDocuments; if (*ptr) { TRI_doc_mptr_t* d = *ptr; if (d->_deletion == 0) { ++result->_matchedDocuments; *qtr++ = *ptr; total++; } } } result->_total = result->_length = total; } //////////////////////////////////////////////////////////////////////////////// /// @brief json representation of a full scan //////////////////////////////////////////////////////////////////////////////// static TRI_json_t* JsonCollectionQuery (TRI_fluent_query_t* qry) { TRI_document_query_t* query; TRI_json_t* json; query = (TRI_document_query_t*) qry; json = TRI_CreateArrayJson(); TRI_Insert2ArrayJson(json, "type", TRI_CreateStringCopyJson("collection")); TRI_Insert2ArrayJson(json, "collection", TRI_CreateStringCopyJson(query->base._collection->_name)); return json; } //////////////////////////////////////////////////////////////////////////////// /// @brief appends a string representation of a full scan //////////////////////////////////////////////////////////////////////////////// static void StringifyCollectionQuery (TRI_fluent_query_t* qry, TRI_string_buffer_t* buffer) { TRI_document_query_t* query; query = (TRI_document_query_t*) qry; TRI_AppendStringStringBuffer(buffer, "collection(\""); TRI_AppendStringStringBuffer(buffer, query->base._collection->_name); TRI_AppendStringStringBuffer(buffer, "\")"); } //////////////////////////////////////////////////////////////////////////////// /// @brief clones a full scan //////////////////////////////////////////////////////////////////////////////// static TRI_fluent_query_t* CloneCollectionQuery (TRI_fluent_query_t* qry) { TRI_collection_query_t* clone; clone = TRI_Allocate(sizeof(TRI_collection_query_t)); CloneQuery(&clone->base, qry); return &clone->base; } //////////////////////////////////////////////////////////////////////////////// /// @brief frees a full scan //////////////////////////////////////////////////////////////////////////////// static void FreeCollectionQuery (TRI_fluent_query_t* query, bool descend) { TRI_Free(query); } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- DISTANCE QUERY // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // --SECTION-- private functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup VocBase /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief optimises a distance query //////////////////////////////////////////////////////////////////////////////// static char* OptimiseDistanceQuery (TRI_fluent_query_t** qry) { TRI_distance_query_t* query; TRI_fluent_query_t* operand; char* e; query = (TRI_distance_query_t*) *qry; // sanity check if (! query->base._collection->_loaded) { return TRI_Concatenate3String("collection \"", query->base._collection->_name, "\" not loaded"); } // check if query is already optimised if (query->base._isOptimised) { return NULL; } // optimise operand e = query->_operand->optimise(&query->_operand); if (e != NULL) { return e; } operand = query->_operand; // ............................................................................. // operand is a NEAR // ............................................................................. if (operand->_type == TRI_QUE_TYPE_NEAR) { TRI_near_query_t* near; near = (TRI_near_query_t*) operand; near->_distance = TRI_DuplicateString(query->_distance); near->base._isOptimised = false; // free the query, but not the operands of the query query->base.free(&query->base, false); // change the query to its operand *qry = &near->base; // recure return (*qry)->optimise(qry); } // ............................................................................. // operand is a WITHIN // ............................................................................. else if (operand->_type == TRI_QUE_TYPE_WITHIN) { TRI_within_query_t* within; within = (TRI_within_query_t*) operand; within->_distance = TRI_DuplicateString(query->_distance); within->base._isOptimised = false; // free the query, but not the operands of the query query->base.free(&query->base, false); // change the query to its operand *qry = &within->base; // recure return (*qry)->optimise(qry); } query->base._isOptimised = true; return NULL; } //////////////////////////////////////////////////////////////////////////////// /// @brief executes a distance query //////////////////////////////////////////////////////////////////////////////// static void ExecuteDistanceQuery (TRI_fluent_query_t* qry, TRI_fluent_query_result_t* result) { TRI_distance_query_t* query; query = (TRI_distance_query_t*) qry; query->_operand->execute(query->_operand, result); } //////////////////////////////////////////////////////////////////////////////// /// @brief json representation of a distance query //////////////////////////////////////////////////////////////////////////////// static TRI_json_t* JsonDistanceQuery (TRI_fluent_query_t* qry) { TRI_json_t* json; TRI_distance_query_t* query; query = (TRI_distance_query_t*) qry; json = TRI_CreateArrayJson(); TRI_Insert2ArrayJson(json, "type", TRI_CreateStringCopyJson("distance")); TRI_Insert2ArrayJson(json, "distance", TRI_CreateStringCopyJson(query->_distance)); TRI_Insert2ArrayJson(json, "operand", query->_operand->json(query->_operand)); return json; } //////////////////////////////////////////////////////////////////////////////// /// @brief appends a string representation of a distance query //////////////////////////////////////////////////////////////////////////////// static void StringifyDistanceQuery (TRI_fluent_query_t* qry, TRI_string_buffer_t* buffer) { TRI_distance_query_t* query; query = (TRI_distance_query_t*) qry; query->_operand->stringify(query->_operand, buffer); TRI_AppendStringStringBuffer(buffer, ".distance("); TRI_AppendStringStringBuffer(buffer, query->_distance); TRI_AppendStringStringBuffer(buffer, ")"); } //////////////////////////////////////////////////////////////////////////////// /// @brief clones a distance query //////////////////////////////////////////////////////////////////////////////// static TRI_fluent_query_t* CloneDistanceQuery (TRI_fluent_query_t* qry) { TRI_distance_query_t* clone; TRI_distance_query_t* query; clone = TRI_Allocate(sizeof(TRI_distance_query_t)); query = (TRI_distance_query_t*) qry; CloneQuery(&clone->base, &query->base); clone->_operand = query->_operand->clone(query->_operand); clone->_distance = TRI_DuplicateString(query->_distance); return &clone->base; } //////////////////////////////////////////////////////////////////////////////// /// @brief frees a distance query //////////////////////////////////////////////////////////////////////////////// static void FreeDistanceQuery (TRI_fluent_query_t* qry, bool descend) { TRI_distance_query_t* query; query = (TRI_distance_query_t*) qry; if (descend) { query->_operand->free(query->_operand, descend); } TRI_FreeString(query->_distance); TRI_Free(query); } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- DOCUMENT QUERY // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // --SECTION-- private functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup VocBase /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief optimises a document lookup //////////////////////////////////////////////////////////////////////////////// static char* OptimiseDocumentQuery (TRI_fluent_query_t** qry) { TRI_document_query_t* query; TRI_col_type_e type; char* e; query = (TRI_document_query_t*) *qry; // sanity check if (! query->base._collection->_loaded) { return TRI_Concatenate3String("collection \"", query->base._collection->_name, "\" not loaded"); } // check if query is already optimised if (query->base._isOptimised) { return NULL; } // underlying query is a collection, use the primary index if (query->_operand != NULL && query->_operand->_type == TRI_QUE_TYPE_COLLECTION) { type = query->base._collection->_collection->base._type; if (type != TRI_COL_TYPE_SIMPLE_DOCUMENT) { return TRI_DuplicateString("cannot handle collection type"); } query->_operand->free(query->_operand, true); query->_operand = NULL; query->base.execute = ExecuteDocumentQueryPrimary; } // optimise operand else { e = query->_operand->optimise(&query->_operand); if (e != NULL) { return e; } } query->base._isOptimised = true; return NULL; } //////////////////////////////////////////////////////////////////////////////// /// @brief executes a document lookup as primary index lookup //////////////////////////////////////////////////////////////////////////////// static void ExecuteDocumentQueryPrimary (TRI_fluent_query_t* qry, TRI_fluent_query_result_t* result) { TRI_doc_mptr_t const* document; TRI_document_query_t* query; TRI_vocbase_col_t const* collection; query = (TRI_document_query_t*) qry; // look up the document collection = query->base._collection; document = collection->_collection->read(collection->_collection, query->_did); // append information about the execution plan TRI_AppendString(&result->_cursor, ">primary"); ++result->_scannedIndexEntries; // free any old storage FreeAugmented(result); FreeDocuments(result); // the document is the result if (document == NULL) { result->_total = result->_length = 0; } else { result->_total = result->_length = 1; ++result->_matchedDocuments; result->_documents = TRI_Allocate(sizeof(TRI_doc_mptr_t*)); *result->_documents = document; } } //////////////////////////////////////////////////////////////////////////////// /// @brief executes a document lookup as full scan //////////////////////////////////////////////////////////////////////////////// static void ExecuteDocumentQuery (TRI_fluent_query_t* qry, TRI_fluent_query_result_t* result) { TRI_doc_mptr_t const** end; TRI_doc_mptr_t const** ptr; TRI_doc_mptr_t const* found; TRI_document_query_t* query; TRI_json_t aug; size_t pos; // get the result set of the underlying query query = (TRI_document_query_t*) qry; // execute the sub-query query->_operand->execute(query->_operand, result); if (result->_error) { return; } // append information about the execution plan TRI_AppendString(&result->_cursor, ">full-scan[document]"); // without any document there will be no match if (result->_length == 0) { return; } // do a full scan pos = 0; ptr = result->_documents; end = result->_documents + result->_length; for (; ptr < end; ++ptr, ++pos) { ++result->_scannedDocuments; // found a match, create new result if ((*ptr)->_did == query->_did) { ++result->_matchedDocuments; if (result->_length == 1) { return; } found = *ptr; TRI_Free(result->_documents); result->_documents = TRI_Allocate(sizeof(TRI_doc_mptr_t*)); result->_total = result->_length = 1; *result->_documents = found; if (result->_augmented != NULL) { TRI_CopyToJson(&aug, &result->_augmented[pos]); FreeAugmented(result); result->_augmented = TRI_Allocate(sizeof(TRI_json_t)); result->_augmented[0] = aug; } return; } } // nothing found result->_total = result->_length = 0; FreeAugmented(result); FreeDocuments(result); } //////////////////////////////////////////////////////////////////////////////// /// @brief json representation of a document lookup //////////////////////////////////////////////////////////////////////////////// static TRI_json_t* JsonDocumentQuery (TRI_fluent_query_t* qry) { TRI_json_t* json; TRI_document_query_t* query; query = (TRI_document_query_t*) qry; json = TRI_CreateArrayJson(); TRI_Insert2ArrayJson(json, "type", TRI_CreateStringCopyJson("document")); TRI_Insert2ArrayJson(json, "identfier", TRI_CreateNumberJson(query->_did)); if (query->_operand == NULL) { TRI_Insert2ArrayJson(json, "collection", TRI_CreateStringCopyJson(query->base._collection->_name)); } else { TRI_Insert2ArrayJson(json, "operand", query->_operand->json(query->_operand)); } return json; } //////////////////////////////////////////////////////////////////////////////// /// @brief appends a string representation of a document lookup //////////////////////////////////////////////////////////////////////////////// static void StringifyDocumentQuery (TRI_fluent_query_t* qry, TRI_string_buffer_t* buffer) { TRI_document_query_t* query; query = (TRI_document_query_t*) qry; if (query->_operand == NULL) { TRI_AppendStringStringBuffer(buffer, "collection(\""); TRI_AppendStringStringBuffer(buffer, query->base._collection->_name); TRI_AppendStringStringBuffer(buffer, "\")"); } else { query->_operand->stringify(query->_operand, buffer); } TRI_AppendStringStringBuffer(buffer, ".document("); TRI_AppendUInt64StringBuffer(buffer, query->_did); TRI_AppendStringStringBuffer(buffer, ")"); } //////////////////////////////////////////////////////////////////////////////// /// @brief clones a document lookup //////////////////////////////////////////////////////////////////////////////// static TRI_fluent_query_t* CloneDocumentQuery (TRI_fluent_query_t* qry) { TRI_document_query_t* clone; TRI_document_query_t* query; clone = TRI_Allocate(sizeof(TRI_document_query_t)); query = (TRI_document_query_t*) qry; CloneQuery(&clone->base, &query->base); clone->_operand = query->_operand == NULL ? NULL : query->_operand->clone(query->_operand); clone->_did = query->_did; return &clone->base; } //////////////////////////////////////////////////////////////////////////////// /// @brief frees a document lookup //////////////////////////////////////////////////////////////////////////////// static void FreeDocumentQuery (TRI_fluent_query_t* qry, bool descend) { TRI_document_query_t* query; query = (TRI_document_query_t*) qry; if (descend) { if (query->_operand != NULL) { query->_operand->free(query->_operand, descend); } } TRI_Free(query); } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- EDGES QUERY // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // --SECTION-- private functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup VocBase /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief optimises an edges lookup //////////////////////////////////////////////////////////////////////////////// static char* OptimiseEdgesQuery (TRI_fluent_query_t** qry) { TRI_edges_query_t* query; char* e; query = (TRI_edges_query_t*) *qry; // sanity check if (! query->base._collection->_loaded) { return TRI_Concatenate3String("collection \"", query->base._collection->_name, "\" not loaded"); } if (query->base._collection->_type != TRI_COL_TYPE_SIMPLE_DOCUMENT) { return TRI_DuplicateString("cannot handle collection type"); } // check if query is already optimised if (query->base._isOptimised) { return NULL; } // optimise operand e = query->_operand->optimise(&query->_operand); if (e != NULL) { return e; } query->base._isOptimised = true; return NULL; } //////////////////////////////////////////////////////////////////////////////// /// @brief executes an edges lookup //////////////////////////////////////////////////////////////////////////////// static void ExecuteEdgesQuery (TRI_fluent_query_t* qry, TRI_fluent_query_result_t* result) { TRI_edges_query_t* query; TRI_sim_collection_t* edgesCollection; TRI_vector_pointer_t v; size_t i; size_t j; query = (TRI_edges_query_t*) qry; // execute the sub-query query->_operand->execute(query->_operand, result); if (result->_error) { return; } TRI_AppendString(&result->_cursor, ">edges"); // without any documents there will be no edges if (result->_length == 0) { return; } // create a vector for the result TRI_InitVectorPointer(&v); edgesCollection = (TRI_sim_collection_t*) query->base._collection->_collection; // for each document get the edges for (i = 0; i < result->_length; ++i) { TRI_doc_mptr_t const* mptr; TRI_vector_pointer_t edges; mptr = result->_documents[i]; edges = TRI_LookupEdgesSimCollection(edgesCollection, query->_direction, query->_operand->_collection->_cid, mptr->_did); for (j = 0; j < edges._length; ++j) { TRI_PushBackVectorPointer(&v, edges._buffer[j]); } TRI_DestroyVectorPointer(&edges); } // free any old results FreeAugmented(result); FreeDocuments(result); result->_documents = (TRI_doc_mptr_t const**) v._buffer; result->_total = result->_length = v._length; result->_scannedDocuments += v._length; } //////////////////////////////////////////////////////////////////////////////// /// @brief json representation of an edges lookup //////////////////////////////////////////////////////////////////////////////// static TRI_json_t* JsonEdgesQuery (TRI_fluent_query_t* qry) { TRI_json_t* json; TRI_edges_query_t* query; query = (TRI_edges_query_t*) qry; json = TRI_CreateArrayJson(); TRI_Insert2ArrayJson(json, "type", TRI_CreateStringCopyJson("edges")); TRI_Insert2ArrayJson(json, "operand", query->_operand->json(query->_operand)); TRI_Insert2ArrayJson(json, "direction", TRI_CreateNumberJson(query->_direction)); TRI_Insert2ArrayJson(json, "edge-collection", TRI_CreateNumberJson(query->base._collection->_cid)); return json; } //////////////////////////////////////////////////////////////////////////////// /// @brief appends a string representation of an edges lookup //////////////////////////////////////////////////////////////////////////////// static void StringifyEdgesQuery (TRI_fluent_query_t* qry, TRI_string_buffer_t* buffer) { TRI_edges_query_t* query; query = (TRI_edges_query_t*) qry; switch (query->_direction) { case TRI_EDGE_IN: TRI_AppendStringStringBuffer(buffer, "inEdges(\""); break; case TRI_EDGE_OUT: TRI_AppendStringStringBuffer(buffer, "outEdges(\""); break; case TRI_EDGE_ANY: TRI_AppendStringStringBuffer(buffer, "edges(\""); break; case TRI_EDGE_UNUSED: TRI_AppendStringStringBuffer(buffer, "nonEdges(\""); break; } TRI_AppendUInt64StringBuffer(buffer, query->base._collection->_cid); TRI_AppendStringStringBuffer(buffer, "\")"); } //////////////////////////////////////////////////////////////////////////////// /// @brief clones an edges lookup //////////////////////////////////////////////////////////////////////////////// static TRI_fluent_query_t* CloneEdgesQuery (TRI_fluent_query_t* qry) { TRI_edges_query_t* clone; TRI_edges_query_t* query; clone = TRI_Allocate(sizeof(TRI_edges_query_t)); query = (TRI_edges_query_t*) qry; CloneQuery(&clone->base, &query->base); clone->_operand = query->_operand == NULL ? NULL : query->_operand->clone(query->_operand); clone->_direction = query->_direction; return &clone->base; } //////////////////////////////////////////////////////////////////////////////// /// @brief frees a edges lookup //////////////////////////////////////////////////////////////////////////////// static void FreeEdgesQuery (TRI_fluent_query_t* qry, bool descend) { TRI_edges_query_t* query; query = (TRI_edges_query_t*) qry; if (descend) { if (query->_operand != NULL) { query->_operand->free(query->_operand, descend); } } TRI_Free(query); } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- GEO INDEX QUERY // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // --SECTION-- private functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup VocBase /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief optimises a geo-spatial hint //////////////////////////////////////////////////////////////////////////////// static char* OptimiseGeoIndexQuery (TRI_fluent_query_t** qry) { TRI_geo_index_query_t* query; char* e; query = (TRI_geo_index_query_t*) *qry; // sanity check if (! query->base._collection->_loaded) { return TRI_Concatenate3String("collection \"", query->base._collection->_name, "\" not loaded"); } // check if query is already optimised if (query->base._isOptimised) { return NULL; } // optimise operand e = query->_operand->optimise(&query->_operand); if (e != NULL) { return e; } query->base._isOptimised = true; return NULL; } //////////////////////////////////////////////////////////////////////////////// /// @brief executes a geo-spatial hint //////////////////////////////////////////////////////////////////////////////// static void ExecuteGeoIndexQuery (TRI_fluent_query_t* qry, TRI_fluent_query_result_t* result) { TRI_geo_index_query_t* query; query = (TRI_geo_index_query_t*) qry; query->_operand->execute(query->_operand, result); } //////////////////////////////////////////////////////////////////////////////// /// @brief json representation of a geo-spatial hint //////////////////////////////////////////////////////////////////////////////// static TRI_json_t* JsonGeoIndexQuery (TRI_fluent_query_t* qry) { TRI_json_t* json; TRI_geo_index_query_t* query; query = (TRI_geo_index_query_t*) qry; json = TRI_CreateArrayJson(); TRI_Insert2ArrayJson(json, "type", TRI_CreateStringCopyJson("geo")); TRI_Insert2ArrayJson(json, "operand", query->_operand->json(query->_operand)); if (query->_location != NULL) { TRI_Insert2ArrayJson(json, "location", TRI_CreateStringCopyJson(query->_location)); TRI_Insert2ArrayJson(json, "geoJson", TRI_CreateBooleanJson(query->_geoJson)); } else if (query->_latitude != NULL && query->_longitude != NULL) { TRI_Insert2ArrayJson(json, "latitude", TRI_CreateStringCopyJson(query->_latitude)); TRI_Insert2ArrayJson(json, "longitude", TRI_CreateStringCopyJson(query->_longitude)); } return json; } //////////////////////////////////////////////////////////////////////////////// /// @brief appends a string representation of a geo-spatial hint //////////////////////////////////////////////////////////////////////////////// static void StringifyGeoIndexQuery (TRI_fluent_query_t* qry, TRI_string_buffer_t* buffer) { TRI_geo_index_query_t* query; query = (TRI_geo_index_query_t*) qry; query->_operand->stringify(query->_operand, buffer); TRI_AppendStringStringBuffer(buffer, ".geo("); if (query->_location != NULL) { TRI_AppendStringStringBuffer(buffer, query->_location); TRI_AppendStringStringBuffer(buffer, ","); TRI_AppendStringStringBuffer(buffer, query->_geoJson ? "true" : "false"); } else if (query->_latitude != NULL && query->_longitude != NULL) { TRI_AppendStringStringBuffer(buffer, query->_latitude); TRI_AppendStringStringBuffer(buffer, ","); TRI_AppendStringStringBuffer(buffer, query->_longitude); } TRI_AppendStringStringBuffer(buffer, ")"); } //////////////////////////////////////////////////////////////////////////////// /// @brief clones a geo-spatial hint //////////////////////////////////////////////////////////////////////////////// static TRI_fluent_query_t* CloneGeoIndexQuery (TRI_fluent_query_t* qry) { TRI_geo_index_query_t* clone; TRI_geo_index_query_t* query; clone = TRI_Allocate(sizeof(TRI_geo_index_query_t)); query = (TRI_geo_index_query_t*) qry; CloneQuery(&clone->base, &query->base); clone->_operand = query->_operand->clone(query->_operand); clone->_location = query->_location == NULL ? NULL : TRI_DuplicateString(query->_location); clone->_latitude = query->_latitude == NULL ? NULL : TRI_DuplicateString(query->_latitude); clone->_longitude = query->_longitude == NULL ? NULL : TRI_DuplicateString(query->_longitude); clone->_geoJson = query->_geoJson; return &clone->base; } //////////////////////////////////////////////////////////////////////////////// /// @brief frees a geo-spatial hint //////////////////////////////////////////////////////////////////////////////// static void FreeGeoIndeyQuery (TRI_fluent_query_t* qry, bool descend) { TRI_geo_index_query_t* query; query = (TRI_geo_index_query_t*) qry; if (descend) { query->_operand->free(query->_operand, descend); } if (query->_location != NULL) { TRI_Free(query->_location); } if (query->_latitude != NULL) { TRI_Free(query->_latitude); } if (query->_longitude != NULL) { TRI_Free(query->_longitude); } TRI_Free(query); } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- LIMIT QUERY // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // --SECTION-- private functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup VocBase /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief optimises a limit query //////////////////////////////////////////////////////////////////////////////// static char* OptimiseLimitQuery (TRI_fluent_query_t** qry) { TRI_limit_query_t* query; TRI_fluent_query_t* operand; char* e; query = (TRI_limit_query_t*) *qry; // sanity check if (! query->base._collection->_loaded) { return TRI_Concatenate3String("collection \"", query->base._collection->_name, "\" not loaded"); } // check if query is already optimised if (query->base._isOptimised) { return NULL; } // optimise operand e = query->_operand->optimise(&query->_operand); if (e != NULL) { return e; } operand = query->_operand; // ............................................................................. // operand is a NEAR // ............................................................................. if (0 <= query->_limit && operand->_type == TRI_QUE_TYPE_NEAR) { TRI_near_query_t* near; near = (TRI_near_query_t*) operand; // no limit specified, that the limit if (near->_limit < 0) { near->_limit = query->_limit; near->base._isOptimised = false; } // modify near query else if (query->_limit < near->_limit) { near->_limit = query->_limit; near->base._isOptimised = false; } // free the query, but not the operands of the query query->base.free(&query->base, false); // change the query to its operand *qry = &near->base; // recure return (*qry)->optimise(qry); } // ............................................................................. // operand is a LIMIT // ............................................................................. else if (operand->_type == TRI_QUE_TYPE_LIMIT) { TRI_limit_query_t* op; op = (TRI_limit_query_t*) operand; // both limits are positive, use minimum if (query->_limit >= 0 && op->_limit >= 0) { if (query->_limit < op->_limit) { op->_limit = query->_limit; op->base._isOptimised = false; } // free the query, but not the operands of the query query->base.free(&query->base, false); // change the query to its operand *qry = &op->base; // recure return (*qry)->optimise(qry); } // both limits are negative, use maximum else if (query->_limit <= 0 && op->_limit <= 0) { if (query->_limit > op->_limit) { op->_limit = query->_limit; op->base._isOptimised = false; } // free the query, but not the operands of the query query->base.free(&query->base, false); // change the query to its operand *qry = &op->base; // recure return (*qry)->optimise(qry); } // operand has a smaller limit, forget query else if (labs(op->_limit) <= labs(query->_limit)) { // free the query, but not the operands of the query query->base.free(&query->base, false); // change the query to its operand *qry = &op->base; return (*qry)->optimise(qry); } } query->base._isOptimised = true; return NULL; } //////////////////////////////////////////////////////////////////////////////// /// @brief executes a limit query //////////////////////////////////////////////////////////////////////////////// static void ExecuteLimitQuery (TRI_fluent_query_t* qry, TRI_fluent_query_result_t* result) { TRI_doc_mptr_t const** copy; TRI_json_t* aug = NULL; TRI_limit_query_t* query; TRI_voc_size_t limit; TRI_voc_size_t offset; query = (TRI_limit_query_t*) qry; // execute the sub-query query->_operand->execute(query->_operand, result); if (result->_error) { return; } // append information about the execution plan TRI_AppendString(&result->_cursor, ">limit"); // without any documents there will be no limit if (result->_length == 0) { return; } // get the last "limit" documents if (query->_limit < 0) { limit = -query->_limit; if (result->_length < limit) { offset = 0; } else { offset = result->_length - limit; } } // get the first "limit" documents else { limit = query->_limit; offset = 0; } // not enough documents to limit query if (result->_length <= limit) { result->_matchedDocuments += result->_length; return; } // shorten result list result->_matchedDocuments += limit; copy = TRI_Allocate(sizeof(TRI_doc_mptr_t*) * limit); memcpy(copy, result->_documents + offset, sizeof(TRI_doc_mptr_t*) * limit); if (result->_augmented != NULL) { aug = TRI_Allocate(sizeof(TRI_json_t) * limit); memcpy(aug, result->_augmented + offset, sizeof(TRI_json_t) * limit); } result->_length = limit; TRI_Free(result->_documents); result->_documents = copy; if (result->_augmented != NULL) { TRI_Free(result->_augmented); result->_augmented = aug; } } //////////////////////////////////////////////////////////////////////////////// /// @brief json representation of a limit query //////////////////////////////////////////////////////////////////////////////// static TRI_json_t* JsonLimitQuery (TRI_fluent_query_t* qry) { TRI_json_t* json; TRI_limit_query_t* query; query = (TRI_limit_query_t*) qry; json = TRI_CreateArrayJson(); TRI_Insert2ArrayJson(json, "type", TRI_CreateStringCopyJson("limit")); TRI_Insert2ArrayJson(json, "limit", TRI_CreateNumberJson(query->_limit)); TRI_Insert2ArrayJson(json, "operand", query->_operand->json(query->_operand)); return json; } //////////////////////////////////////////////////////////////////////////////// /// @brief appends a string representation of a limit query //////////////////////////////////////////////////////////////////////////////// static void StringifyLimitQuery (TRI_fluent_query_t* qry, TRI_string_buffer_t* buffer) { TRI_limit_query_t* query; query = (TRI_limit_query_t*) qry; query->_operand->stringify(query->_operand, buffer); TRI_AppendStringStringBuffer(buffer, ".limit("); TRI_AppendInt64StringBuffer(buffer, query->_limit); TRI_AppendStringStringBuffer(buffer, ")"); } //////////////////////////////////////////////////////////////////////////////// /// @brief clones a limit query //////////////////////////////////////////////////////////////////////////////// static TRI_fluent_query_t* CloneLimitQuery (TRI_fluent_query_t* qry) { TRI_limit_query_t* clone; TRI_limit_query_t* query; clone = TRI_Allocate(sizeof(TRI_limit_query_t)); query = (TRI_limit_query_t*) qry; CloneQuery(&clone->base, &query->base); clone->_operand = query->_operand->clone(query->_operand); clone->_limit = query->_limit; return &clone->base; } //////////////////////////////////////////////////////////////////////////////// /// @brief frees a limit query //////////////////////////////////////////////////////////////////////////////// static void FreeLimitQuery (TRI_fluent_query_t* qry, bool descend) { TRI_limit_query_t* query; query = (TRI_limit_query_t*) qry; if (descend) { query->_operand->free(query->_operand, descend); } TRI_Free(query); } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- NEAR QUERY // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // --SECTION-- private types // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup VocBase /// @{ //////////////////////////////////////////////////////////////////////////////// typedef struct { double _distance; void const* _data; } geo_coordinate_distance_t; //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- private functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup VocBase /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief locates a suitable geo index //////////////////////////////////////////////////////////////////////////////// static TRI_geo_index_t* LocateGeoIndex (TRI_fluent_query_t* query) { TRI_col_type_e type; TRI_index_t* idx2; TRI_index_t* idx; TRI_sim_collection_t* collection; size_t i; // sanity check if (! query->_collection->_loaded) { return NULL; } type = query->_collection->_collection->base._type; if (type != TRI_COL_TYPE_SIMPLE_DOCUMENT) { return NULL; } collection = (TRI_sim_collection_t*) query->_collection->_collection; // return the geo index idx = NULL; for (i = 0; i < collection->_indexes._length; ++i) { idx2 = collection->_indexes._buffer[i]; if (idx2->_type == TRI_IDX_TYPE_GEO_INDEX) { if (idx == NULL) { idx = idx2; } else if (idx2->_iid < idx->_iid) { idx = idx2; } } } return (TRI_geo_index_t*) idx; } //////////////////////////////////////////////////////////////////////////////// /// @brief sorts geo coordinates //////////////////////////////////////////////////////////////////////////////// static int CompareGeoCoordinateDistance (geo_coordinate_distance_t* left, geo_coordinate_distance_t* right) { if (left->_distance < right->_distance) { return -1; } else if (left->_distance > right->_distance) { return 1; } else { return 0; } } #define FSRT_NAME SortGeoCoordinates #define FSRT_TYPE geo_coordinate_distance_t #define FSRT_COMP(l,r,s) CompareGeoCoordinateDistance(l,r) uint32_t FSRT_Rand = 0; static uint32_t RandomGeoCoordinateDistance (void) { return (FSRT_Rand = FSRT_Rand * 31415 + 27818); } #define FSRT__RAND \ ((fs_b) + FSRT__UNIT * (RandomGeoCoordinateDistance() % FSRT__DIST(fs_e,fs_b,FSRT__SIZE))) #include //////////////////////////////////////////////////////////////////////////////// /// @brief uses a specific geo-spatial index //////////////////////////////////////////////////////////////////////////////// static TRI_geo_index_t* LookupGeoIndex (TRI_geo_index_query_t* query) { TRI_col_type_e type; TRI_doc_collection_t* collection; TRI_index_t* idx; TRI_shape_pid_t lat = 0; TRI_shape_pid_t loc = 0; TRI_shape_pid_t lon = 0; TRI_shaper_t* shaper; TRI_sim_collection_t* sim; // lookup an geo index collection = query->base._collection->_collection; type = collection->base._type; if (type != TRI_COL_TYPE_SIMPLE_DOCUMENT) { LOG_ERROR("cannot handle collection type %ld", collection->base._type); return NULL; } sim = (TRI_sim_collection_t*) collection; shaper = query->base._collection->_collection->_shaper; if (query->_location != NULL) { loc = shaper->findAttributePathByName(shaper, query->_location); } if (query->_latitude != NULL) { lat = shaper->findAttributePathByName(shaper, query->_latitude); } if (query->_longitude != NULL) { lon = shaper->findAttributePathByName(shaper, query->_longitude); } if (query->_location != NULL) { idx = TRI_LookupGeoIndexSimCollection(sim, loc, query->_geoJson); } else if (query->_longitude != NULL && query->_latitude != NULL) { idx = TRI_LookupGeoIndex2SimCollection(sim, lat, lon); } else { idx = NULL; } return (TRI_geo_index_t*) idx; } //////////////////////////////////////////////////////////////////////////////// /// @brief create result //////////////////////////////////////////////////////////////////////////////// static void GeoResult (GeoCoordinates* cors, char const* distance, TRI_fluent_query_result_t* result) { GeoCoordinate* end; GeoCoordinate* ptr; TRI_doc_mptr_t const** wtr; double* dtr; geo_coordinate_distance_t* gnd; geo_coordinate_distance_t* gtr; geo_coordinate_distance_t* tmp; size_t n; if (cors == NULL) { result->_error = TRI_DuplicateString("cannot execute geo index search"); return; } // sort the result n = cors->length; gtr = (tmp = TRI_Allocate(sizeof(geo_coordinate_distance_t) * n)); gnd = tmp + n; ptr = cors->coordinates; end = cors->coordinates + n; dtr = cors->distances; for (; ptr < end; ++ptr, ++dtr, ++gtr) { gtr->_distance = *dtr; gtr->_data = ptr->data; } GeoIndex_CoordinatesFree(cors); SortGeoCoordinates(tmp, gnd); // copy the documents result->_documents = (TRI_doc_mptr_t const**) (wtr = TRI_Allocate(sizeof(TRI_doc_mptr_t*) * n)); for (gtr = tmp; gtr < gnd; ++gtr, ++wtr) { *wtr = gtr->_data; } // copy the distance if (distance != NULL) { TRI_json_t* atr; result->_augmented = (atr = TRI_Allocate(sizeof(TRI_json_t) * n)); for (gtr = tmp; gtr < gnd; ++gtr, ++atr) { atr->_type = TRI_JSON_ARRAY; TRI_InitVector(&atr->_value._objects, sizeof(TRI_json_t)); TRI_InsertArrayJson(atr, distance, TRI_CreateNumberJson(gtr->_distance)); } } TRI_Free(tmp); result->_total = result->_length = n; result->_scannedIndexEntries += n; } //////////////////////////////////////////////////////////////////////////////// /// @brief optimises a near query //////////////////////////////////////////////////////////////////////////////// static char* OptimiseNearQuery (TRI_fluent_query_t** qry) { TRI_near_query_t* query; char* e; query = (TRI_near_query_t*) *qry; // sanity check if (! query->base._collection->_loaded) { return TRI_Concatenate3String("collection \"", query->base._collection->_name, "\" not loaded"); } // check if query is already optimised if (query->base._isOptimised) { return NULL; } // optimise operand e = query->_operand->optimise(&query->_operand); if (e != NULL) { return e; } query->base._isOptimised = true; return NULL; } //////////////////////////////////////////////////////////////////////////////// /// @brief executes a near query //////////////////////////////////////////////////////////////////////////////// static void ExecuteNearQuery (TRI_fluent_query_t* qry, TRI_fluent_query_result_t* result) { static size_t const DEFAULT_LIMIT = 100; GeoCoordinates* cors; TRI_geo_index_t* idx; TRI_near_query_t* query; TRI_fluent_query_t* operand; query = (TRI_near_query_t*) qry; operand = query->_operand; // free any old results FreeAugmented(result); FreeDocuments(result); // we need a suitable geo index if (operand->_type == TRI_QUE_TYPE_GEO_INDEX) { TRI_geo_index_query_t* geo; geo = (TRI_geo_index_query_t*) operand; idx = LookupGeoIndex(geo); operand = geo->_operand; } else { idx = LocateGeoIndex(&query->base); } if (idx == NULL) { result->_error = TRI_DuplicateString("cannot locate suitable geo index"); return; } // sub-query must be the collection if (operand->_type != TRI_QUE_TYPE_COLLECTION) { result->_error = TRI_DuplicateString("near query can only be executed for the complete collection"); return; } // append information about the execution plan TRI_AppendString(&result->_cursor, ">near"); // execute geo search if (query->_limit < 0) { cors = TRI_NearestGeoIndex(&idx->base, query->_latitude, query->_longitude, DEFAULT_LIMIT); } else { cors = TRI_NearestGeoIndex(&idx->base, query->_latitude, query->_longitude, query->_limit); } // and append result GeoResult(cors, query->_distance, result); } //////////////////////////////////////////////////////////////////////////////// /// @brief json representation of a near lookup //////////////////////////////////////////////////////////////////////////////// static TRI_json_t* JsonNearQuery (TRI_fluent_query_t* qry) { TRI_json_t* json; TRI_near_query_t* query; query = (TRI_near_query_t*) qry; json = TRI_CreateArrayJson(); TRI_Insert2ArrayJson(json, "type", TRI_CreateStringCopyJson("near")); TRI_Insert2ArrayJson(json, "operand", query->_operand->json(query->_operand)); TRI_Insert2ArrayJson(json, "latitude", TRI_CreateNumberJson(query->_latitude)); TRI_Insert2ArrayJson(json, "longitude", TRI_CreateNumberJson(query->_longitude)); TRI_Insert2ArrayJson(json, "limit", TRI_CreateNumberJson(query->_limit)); if (query->_distance != NULL) { TRI_Insert2ArrayJson(json, "distance", TRI_CreateStringCopyJson(query->_distance)); } return json; } //////////////////////////////////////////////////////////////////////////////// /// @brief appends a string representation of a near query //////////////////////////////////////////////////////////////////////////////// static void StringifyNearQuery (TRI_fluent_query_t* qry, TRI_string_buffer_t* buffer) { TRI_near_query_t* query; query = (TRI_near_query_t*) qry; query->_operand->stringify(query->_operand, buffer); TRI_AppendStringStringBuffer(buffer, ".near("); TRI_AppendDoubleStringBuffer(buffer, query->_latitude); TRI_AppendStringStringBuffer(buffer, ","); TRI_AppendDoubleStringBuffer(buffer, query->_longitude); TRI_AppendStringStringBuffer(buffer, ")"); if (query->_distance != NULL) { TRI_AppendStringStringBuffer(buffer, ".distance("); TRI_AppendStringStringBuffer(buffer, query->_distance); TRI_AppendStringStringBuffer(buffer, ")"); } if (0 <= query->_limit) { TRI_AppendStringStringBuffer(buffer, ".limit("); TRI_AppendUInt64StringBuffer(buffer, query->_limit); TRI_AppendStringStringBuffer(buffer, ")"); } } //////////////////////////////////////////////////////////////////////////////// /// @brief clones a near query //////////////////////////////////////////////////////////////////////////////// static TRI_fluent_query_t* CloneNearQuery (TRI_fluent_query_t* qry) { TRI_near_query_t* clone; TRI_near_query_t* query; clone = TRI_Allocate(sizeof(TRI_near_query_t)); query = (TRI_near_query_t*) qry; CloneQuery(&clone->base, &query->base); clone->_operand = query->_operand->clone(query->_operand); clone->_latitude = query->_latitude; clone->_longitude = query->_longitude; clone->_limit = query->_limit; clone->_distance = query->_distance == NULL ? NULL : TRI_DuplicateString(query->_distance); return &clone->base; } //////////////////////////////////////////////////////////////////////////////// /// @brief frees a near query //////////////////////////////////////////////////////////////////////////////// static void FreeNearQuery (TRI_fluent_query_t* qry, bool descend) { TRI_near_query_t* query; query = (TRI_near_query_t*) qry; if (descend) { query->_operand->free(query->_operand, descend); } TRI_Free(query); } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- SELECT BY EXAMPLE QUERY // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // --SECTION-- private functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup VocBase /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief checks for match of an example //////////////////////////////////////////////////////////////////////////////// static bool IsExampleMatch (TRI_shaper_t* shaper, TRI_doc_mptr_t const* doc, size_t len, TRI_shape_pid_t* pids, TRI_shaped_json_t** values) { TRI_shape_access_t const* accessor; TRI_shaped_json_t const* document; TRI_shaped_json_t* example; TRI_shaped_json_t result; bool ok; size_t i; document = &doc->_document; for (i = 0; i < len; ++i) { example = values[i]; accessor = TRI_FindAccessorVocShaper(shaper, document->_sid, pids[i]); if (accessor == NULL) { LOG_TRACE("failed to get accessor for sid %lu and path %lu", (unsigned long) document->_sid, (unsigned long) pids[i]); return false; } if (accessor->_shape == NULL) { LOG_TRACE("expecting an array for path %lu", (unsigned long) pids[i]); return false; } if (accessor->_shape->_sid != example->_sid) { LOG_TRACE("expecting sid %lu for path %lu, got sid %lu", (unsigned long) example->_sid, (unsigned long) pids[i], (unsigned long) accessor->_shape->_sid); return false; } ok = TRI_ExecuteShapeAccessor(accessor, document, &result); if (! ok) { LOG_TRACE("failed to get accessor for sid %lu and path %lu", (unsigned long) document->_sid, (unsigned long) pids[i]); return false; } if (result._data.length != example->_data.length) { LOG_TRACE("expecting length %lu, got length %lu for path %lu", (unsigned long) result._data.length, (unsigned long) example->_data.length, (unsigned long) pids[i]); return false; } if (memcmp(result._data.data, example->_data.data, example->_data.length) != 0) { LOG_TRACE("data mismatch at path %lu", (unsigned long) pids[i]); return false; } } return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief optimises a select-by-example query //////////////////////////////////////////////////////////////////////////////// static char* OptimiseSelectFullQuery (TRI_fluent_query_t** qry) { TRI_select_full_query_t* query; char* e; query = (TRI_select_full_query_t*) *qry; // sanity check if (! query->base._collection->_loaded) { return TRI_Concatenate3String("collection \"", query->base._collection->_name, "\" not loaded"); } // check if query is already optimised if (query->base._isOptimised) { return NULL; } // optimise operand e = query->_operand->optimise(&query->_operand); if (e != NULL) { return e; } query->base._isOptimised = true; return NULL; } //////////////////////////////////////////////////////////////////////////////// /// @brief executes a select-by-example query //////////////////////////////////////////////////////////////////////////////// static void ExecuteSelectFullQuery (TRI_fluent_query_t* qry, TRI_fluent_query_result_t* result) { union { void* v; void const* c; } cnv; TRI_select_full_query_t* query; TRI_vector_pointer_t filtered; TRI_shaper_t* shaper; TRI_doc_mptr_t const** ptr; TRI_doc_mptr_t const** end; query = (TRI_select_full_query_t*) qry; // execute the sub-query query->_operand->execute(query->_operand, result); if (result->_error) { return; } TRI_AppendString(&result->_cursor, ">select[full-scan]"); // without any document there will be no match if (result->_length == 0) { return; } // do a full scan TRI_InitVectorPointer(&filtered); shaper = query->base._collection->_collection->_shaper; ptr = result->_documents; end = result->_documents + result->_length; for (; ptr < end; ++ptr) { ++result->_scannedDocuments; if (IsExampleMatch(shaper, *ptr, query->_length, query->_pids, query->_values)) { ++result->_matchedDocuments; cnv.c = *ptr; TRI_PushBackVectorPointer(&filtered, cnv.v); } } TRI_Free(result->_documents); result->_documents = (TRI_doc_mptr_t const**) filtered._buffer; result->_length = filtered._length; if (result->_augmented != NULL) { TRI_Free(result->_augmented); result->_augmented = NULL; } } //////////////////////////////////////////////////////////////////////////////// /// @brief json representation of a select-by-example query //////////////////////////////////////////////////////////////////////////////// static TRI_json_t* JsonSelectFullQuery (TRI_fluent_query_t* qry) { TRI_json_t* json; TRI_json_t* example; TRI_select_full_query_t* query; TRI_shaper_t* shaper; size_t i; query = (TRI_select_full_query_t*) qry; json = TRI_CreateArrayJson(); shaper = query->base._collection->_collection->_shaper; TRI_Insert2ArrayJson(json, "type", TRI_CreateStringCopyJson("select-full")); example = TRI_CreateArrayJson(); for (i = 0; i < query->_length; ++i) { char const* path; TRI_json_t* value; path = TRI_AttributeNameShapePid(shaper, query->_pids[i]); value = TRI_JsonShapedJson(shaper, query->_values[i]); TRI_Insert2ArrayJson(example, path, value); } TRI_Insert2ArrayJson(json, "example", example); return json; } //////////////////////////////////////////////////////////////////////////////// /// @brief appends a string representation of a select-by-example query //////////////////////////////////////////////////////////////////////////////// static void StringifySelectFullQuery (TRI_fluent_query_t* qry, TRI_string_buffer_t* buffer) { TRI_select_full_query_t* query; query = (TRI_select_full_query_t*) qry; query->_operand->stringify(query->_operand, buffer); TRI_AppendStringStringBuffer(buffer, ".select("); // TODO TRI_AppendStringStringBuffer(buffer, ")"); } //////////////////////////////////////////////////////////////////////////////// /// @brief clones a select-by-example query //////////////////////////////////////////////////////////////////////////////// static TRI_fluent_query_t* CloneSelectFullQuery (TRI_fluent_query_t* qry) { TRI_select_full_query_t* clone; TRI_select_full_query_t* query; TRI_shaped_json_t* copy; size_t n; size_t i; clone = TRI_Allocate(sizeof(TRI_select_full_query_t)); query = (TRI_select_full_query_t*) qry; CloneQuery(&clone->base, &query->base); clone->_operand = query->_operand->clone(query->_operand); n = query->_length; clone->_length = n; clone->_pids = TRI_Allocate(n * sizeof(TRI_shape_pid_t)); clone->_values = TRI_Allocate(n * sizeof(TRI_shaped_json_t*)); for (i = 0; i < n; ++i) { copy = TRI_Allocate(sizeof(TRI_shaped_json_t)); copy->_sid = query->_values[i]->_sid; TRI_CopyToBlob(©->_data, TRI_CopyBlob(&query->_values[i]->_data)); clone->_values[i] = copy; clone->_pids[i] = query->_pids[i]; } return &clone->base; } //////////////////////////////////////////////////////////////////////////////// /// @brief frees a select-by-example query //////////////////////////////////////////////////////////////////////////////// static void FreeSelectFullQuery (TRI_fluent_query_t* qry, bool descend) { TRI_select_full_query_t* query; size_t i; query = (TRI_select_full_query_t*) qry; if (descend) { query->_operand->free(query->_operand, descend); } for (i = 0; i < query->_length; ++i) { TRI_FreeShapedJson(query->_values[i]); } TRI_Free(query->_values); TRI_Free(query->_pids); TRI_Free(query); } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- SKIP QUERY // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // --SECTION-- private functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup VocBase /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief optimises a skip query //////////////////////////////////////////////////////////////////////////////// static char* OptimiseSkipQuery (TRI_fluent_query_t** qry) { TRI_skip_query_t* query; TRI_fluent_query_t* operand; char* e; query = (TRI_skip_query_t*) *qry; // sanity check if (! query->base._collection->_loaded) { return TRI_Concatenate3String("collection \"", query->base._collection->_name, "\" not loaded"); } // check if query is already optimised if (query->base._isOptimised) { return NULL; } // optimise operand e = query->_operand->optimise(&query->_operand); if (e != NULL) { return e; } operand = query->_operand; // ............................................................................. // no skip at all // ............................................................................. if (query->_skip == 0) { TRI_skip_query_t* op; op = (TRI_skip_query_t*) operand; // free the query, but not the operands of the query query->base.free(&query->base, false); // change the query to its operand *qry = &op->base; // done return NULL; } // ............................................................................. // operand is a SKIP // ............................................................................. if (operand->_type == TRI_QUE_TYPE_SKIP) { TRI_skip_query_t* op; op = (TRI_skip_query_t*) operand; op->_skip = op->_skip + query->_skip; op->base._isOptimised = false; // free the query, but not the operands of the query query->base.free(&query->base, false); // change the query to its operand *qry = &op->base; // recure return (*qry)->optimise(qry); } query->base._isOptimised = true; return NULL; } //////////////////////////////////////////////////////////////////////////////// /// @brief executes a skip query //////////////////////////////////////////////////////////////////////////////// static void ExecuteSkipQuery (TRI_fluent_query_t* qry, TRI_fluent_query_result_t* result) { TRI_doc_mptr_t const** copy; TRI_json_t* aug = NULL; TRI_skip_query_t* query; TRI_voc_size_t newtotal; query = (TRI_skip_query_t*) qry; // execute the sub-query query->_operand->execute(query->_operand, result); if (result->_error) { return; } TRI_AppendString(&result->_cursor, ">skip"); // no skip at all if (query->_skip == 0) { result->_matchedDocuments += result->_length; return; } // without any documents there will be no skipping if (result->_length == 0) { return; } // not enough documents if (result->_length <= query->_skip) { result->_length = 0; FreeAugmented(result); FreeDocuments(result); return; } // shorten result list newtotal = (result->_length - query->_skip); result->_matchedDocuments += newtotal; copy = TRI_Allocate(sizeof(TRI_doc_mptr_t*) * newtotal); memcpy(copy, result->_documents + query->_skip, sizeof(TRI_doc_mptr_t*) * newtotal); if (result->_augmented != NULL) { aug = TRI_Allocate(sizeof(TRI_json_t) * newtotal); memcpy(aug, result->_augmented + query->_skip, sizeof(TRI_json_t) * newtotal); } result->_length = newtotal; TRI_Free(result->_documents); result->_documents = copy; if (result->_augmented != NULL) { TRI_Free(result->_augmented); result->_augmented = aug; } } //////////////////////////////////////////////////////////////////////////////// /// @brief json representation of a skip query //////////////////////////////////////////////////////////////////////////////// static TRI_json_t* JsonSkipQuery (TRI_fluent_query_t* qry) { TRI_json_t* json; TRI_skip_query_t* query; query = (TRI_skip_query_t*) qry; json = TRI_CreateArrayJson(); TRI_Insert2ArrayJson(json, "type", TRI_CreateStringCopyJson("skip")); TRI_Insert2ArrayJson(json, "skip", TRI_CreateNumberJson(query->_skip)); TRI_Insert2ArrayJson(json, "operand", query->_operand->json(query->_operand)); return json; } //////////////////////////////////////////////////////////////////////////////// /// @brief appends a string representation of a skip query //////////////////////////////////////////////////////////////////////////////// static void StringifySkipQuery (TRI_fluent_query_t* qry, TRI_string_buffer_t* buffer) { TRI_skip_query_t* query; query = (TRI_skip_query_t*) qry; query->_operand->stringify(query->_operand, buffer); TRI_AppendStringStringBuffer(buffer, ".skip("); TRI_AppendUInt64StringBuffer(buffer, query->_skip); TRI_AppendStringStringBuffer(buffer, ")"); } //////////////////////////////////////////////////////////////////////////////// /// @brief clones a skip query //////////////////////////////////////////////////////////////////////////////// static TRI_fluent_query_t* CloneSkipQuery (TRI_fluent_query_t* qry) { TRI_skip_query_t* clone; TRI_skip_query_t* query; clone = TRI_Allocate(sizeof(TRI_skip_query_t)); query = (TRI_skip_query_t*) qry; CloneQuery(&clone->base, &query->base); clone->_operand = query->_operand->clone(query->_operand); clone->_skip = query->_skip; return &clone->base; } //////////////////////////////////////////////////////////////////////////////// /// @brief frees a skip query //////////////////////////////////////////////////////////////////////////////// static void FreeSkipQuery (TRI_fluent_query_t* qry, bool descend) { TRI_skip_query_t* query; query = (TRI_skip_query_t*) qry; if (descend) { query->_operand->free(query->_operand, descend); } TRI_Free(query); } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- WITHIN QUERY // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // --SECTION-- private functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup VocBase /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief optimises a within query //////////////////////////////////////////////////////////////////////////////// static char* OptimiseWithinQuery (TRI_fluent_query_t** qry) { TRI_within_query_t* query; char* e; query = (TRI_within_query_t*) *qry; // sanity check if (! query->base._collection->_loaded) { return TRI_Concatenate3String("collection \"", query->base._collection->_name, "\" not loaded"); } // check if query is already optimised if (query->base._isOptimised) { return NULL; } // optimise operand e = query->_operand->optimise(&query->_operand); if (e != NULL) { return e; } query->base._isOptimised = true; return NULL; } //////////////////////////////////////////////////////////////////////////////// /// @brief executes a within query //////////////////////////////////////////////////////////////////////////////// static void ExecuteWithinQuery (TRI_fluent_query_t* qry, TRI_fluent_query_result_t* result) { GeoCoordinates* cors; TRI_geo_index_t* idx; TRI_within_query_t* query; TRI_fluent_query_t* operand; query = (TRI_within_query_t*) qry; operand = query->_operand; // free any old results FreeAugmented(result); FreeDocuments(result); // we need a suitable geo index if (operand->_type == TRI_QUE_TYPE_GEO_INDEX) { TRI_geo_index_query_t* geo; geo = (TRI_geo_index_query_t*) operand; idx = LookupGeoIndex(geo); operand = geo->_operand; } else { idx = LocateGeoIndex(&query->base); } if (idx == NULL) { result->_error = TRI_DuplicateString("cannot locate suitable geo index"); return; } // sub-query must be the collection if (operand->_type != TRI_QUE_TYPE_COLLECTION) { result->_error = TRI_DuplicateString("within query can only be executed for the complete collection"); return; } // append information about the execution plan TRI_AppendString(&result->_cursor, ">within"); // execute geo search cors = TRI_WithinGeoIndex(&idx->base, query->_latitude, query->_longitude, query->_radius); // and create result GeoResult(cors, query->_distance, result); } //////////////////////////////////////////////////////////////////////////////// /// @brief json representation of a within lookup //////////////////////////////////////////////////////////////////////////////// static TRI_json_t* JsonWithinQuery (TRI_fluent_query_t* qry) { TRI_json_t* json; TRI_within_query_t* query; query = (TRI_within_query_t*) qry; json = TRI_CreateArrayJson(); TRI_Insert2ArrayJson(json, "type", TRI_CreateStringCopyJson("within")); TRI_Insert2ArrayJson(json, "operand", query->_operand->json(query->_operand)); TRI_Insert2ArrayJson(json, "latitude", TRI_CreateNumberJson(query->_latitude)); TRI_Insert2ArrayJson(json, "longitude", TRI_CreateNumberJson(query->_longitude)); TRI_Insert2ArrayJson(json, "radius", TRI_CreateNumberJson(query->_radius)); if (query->_distance != NULL) { TRI_Insert2ArrayJson(json, "distance", TRI_CreateStringCopyJson(query->_distance)); } return json; } //////////////////////////////////////////////////////////////////////////////// /// @brief appends a string representation of a within query //////////////////////////////////////////////////////////////////////////////// static void StringifyWithinQuery (TRI_fluent_query_t* qry, TRI_string_buffer_t* buffer) { TRI_within_query_t* query; query = (TRI_within_query_t*) qry; query->_operand->stringify(query->_operand, buffer); TRI_AppendStringStringBuffer(buffer, ".within("); TRI_AppendDoubleStringBuffer(buffer, query->_latitude); TRI_AppendStringStringBuffer(buffer, ","); TRI_AppendDoubleStringBuffer(buffer, query->_longitude); TRI_AppendStringStringBuffer(buffer, ","); TRI_AppendDoubleStringBuffer(buffer, query->_radius); TRI_AppendStringStringBuffer(buffer, ")"); if (query->_distance != NULL) { TRI_AppendStringStringBuffer(buffer, ".distance("); TRI_AppendStringStringBuffer(buffer, query->_distance); TRI_AppendStringStringBuffer(buffer, ")"); } } //////////////////////////////////////////////////////////////////////////////// /// @brief clones a within query //////////////////////////////////////////////////////////////////////////////// static TRI_fluent_query_t* CloneWithinQuery (TRI_fluent_query_t* qry) { TRI_within_query_t* clone; TRI_within_query_t* query; clone = TRI_Allocate(sizeof(TRI_within_query_t)); query = (TRI_within_query_t*) qry; CloneQuery(&clone->base, &query->base); clone->_operand = query->_operand->clone(query->_operand); clone->_latitude = query->_latitude; clone->_longitude = query->_longitude; clone->_radius = query->_radius; clone->_distance = query->_distance == NULL ? NULL : TRI_DuplicateString(query->_distance); return &clone->base; } //////////////////////////////////////////////////////////////////////////////// /// @brief frees a within query //////////////////////////////////////////////////////////////////////////////// static void FreeWithinQuery (TRI_fluent_query_t* qry, bool descend) { TRI_within_query_t* query; query = (TRI_within_query_t*) qry; if (descend) { query->_operand->free(query->_operand, descend); } TRI_Free(query); } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- constructors and destructors // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup VocBase /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief creates a full scan of a collection //////////////////////////////////////////////////////////////////////////////// TRI_fluent_query_t* TRI_CreateCollectionQuery (TRI_vocbase_col_t const* collection) { TRI_collection_query_t* query; query = TRI_Allocate(sizeof(TRI_collection_query_t)); query->base._type = TRI_QUE_TYPE_COLLECTION; query->base._collection = collection; query->base._isOptimised = false; query->base.optimise = OptimiseCollectionQuery; query->base.execute = ExecuteCollectionQuery; query->base.json = JsonCollectionQuery; query->base.stringify = StringifyCollectionQuery; query->base.clone = CloneCollectionQuery; query->base.free = FreeCollectionQuery; return &query->base; } //////////////////////////////////////////////////////////////////////////////// /// @brief adds the distance to a query //////////////////////////////////////////////////////////////////////////////// TRI_fluent_query_t* TRI_CreateDistanceQuery (TRI_fluent_query_t* operand, char const* distance) { TRI_distance_query_t* query; query = TRI_Allocate(sizeof(TRI_distance_query_t)); query->base._type = TRI_QUE_TYPE_DISTANCE; query->base._collection = operand->_collection; query->base._isOptimised = false; query->base.optimise = OptimiseDistanceQuery; query->base.execute = ExecuteDistanceQuery; query->base.json = JsonDistanceQuery; query->base.stringify = StringifyDistanceQuery; query->base.clone = CloneDistanceQuery; query->base.free = FreeDistanceQuery; query->_operand = operand; query->_distance = TRI_DuplicateString(distance); return &query->base; } //////////////////////////////////////////////////////////////////////////////// /// @brief looks up a document //////////////////////////////////////////////////////////////////////////////// TRI_fluent_query_t* TRI_CreateDocumentQuery (TRI_fluent_query_t* operand, TRI_voc_did_t did) { TRI_document_query_t* query; query = TRI_Allocate(sizeof(TRI_document_query_t)); query->base._type = TRI_QUE_TYPE_DOCUMENT; query->base._collection = operand->_collection; query->base._isOptimised = false; query->base.optimise = OptimiseDocumentQuery; query->base.execute = ExecuteDocumentQuery; query->base.json = JsonDocumentQuery; query->base.stringify = StringifyDocumentQuery; query->base.clone = CloneDocumentQuery; query->base.free = FreeDocumentQuery; query->_operand = operand; query->_did = did; return &query->base; } //////////////////////////////////////////////////////////////////////////////// /// @brief looks up edges //////////////////////////////////////////////////////////////////////////////// TRI_fluent_query_t* TRI_CreateEdgesQuery (TRI_vocbase_col_t const* edges, TRI_fluent_query_t* operand, TRI_edge_direction_e direction) { TRI_edges_query_t* query; query = TRI_Allocate(sizeof(TRI_edges_query_t)); query->base._type = TRI_QUE_TYPE_EDGES; query->base._collection = edges; query->base._isOptimised = false; query->base.optimise = OptimiseEdgesQuery; query->base.execute = ExecuteEdgesQuery; query->base.json = JsonEdgesQuery; query->base.stringify = StringifyEdgesQuery; query->base.clone = CloneEdgesQuery; query->base.free = FreeEdgesQuery; query->_operand = operand; query->_direction = direction; return &query->base; } //////////////////////////////////////////////////////////////////////////////// /// @brief looks up an geo-spatial index as hint //////////////////////////////////////////////////////////////////////////////// TRI_fluent_query_t* TRI_CreateGeoIndexQuery (TRI_fluent_query_t* operand, char const* location, char const* latitude, char const* longitude, bool geoJson) { TRI_geo_index_query_t* query; query = TRI_Allocate(sizeof(TRI_geo_index_query_t)); query->base._type = TRI_QUE_TYPE_GEO_INDEX; query->base._collection = operand->_collection; query->base._isOptimised = false; query->base.optimise = OptimiseGeoIndexQuery; query->base.execute = ExecuteGeoIndexQuery; query->base.json = JsonGeoIndexQuery; query->base.stringify = StringifyGeoIndexQuery; query->base.clone = CloneGeoIndexQuery; query->base.free = FreeGeoIndeyQuery; query->_operand = operand; query->_location = (location == NULL ? NULL : TRI_DuplicateString(location)); query->_geoJson = geoJson; query->_latitude = (latitude == NULL ? NULL : TRI_DuplicateString(latitude)); query->_longitude = (longitude == NULL ? NULL : TRI_DuplicateString(longitude)); return &query->base; } //////////////////////////////////////////////////////////////////////////////// /// @brief limits an existing query //////////////////////////////////////////////////////////////////////////////// TRI_fluent_query_t* TRI_CreateLimitQuery (TRI_fluent_query_t* operand, TRI_voc_ssize_t limit) { TRI_limit_query_t* query; query = TRI_Allocate(sizeof(TRI_limit_query_t)); query->base._type = TRI_QUE_TYPE_LIMIT; query->base._collection = operand->_collection; query->base._isOptimised = false; query->base.optimise = OptimiseLimitQuery; query->base.execute = ExecuteLimitQuery; query->base.json = JsonLimitQuery; query->base.stringify = StringifyLimitQuery; query->base.clone = CloneLimitQuery; query->base.free = FreeLimitQuery; query->_operand = operand; query->_limit = limit; return &query->base; } //////////////////////////////////////////////////////////////////////////////// /// @brief looks up documents near a given point //////////////////////////////////////////////////////////////////////////////// TRI_fluent_query_t* TRI_CreateNearQuery (TRI_fluent_query_t* operand, double latitude, double longitude, char const* distance) { TRI_near_query_t* query; query = TRI_Allocate(sizeof(TRI_near_query_t)); query->base._type = TRI_QUE_TYPE_NEAR; query->base._collection = operand->_collection; query->base._isOptimised = false; query->base.optimise = OptimiseNearQuery; query->base.execute = ExecuteNearQuery; query->base.json = JsonNearQuery; query->base.stringify = StringifyNearQuery; query->base.clone = CloneNearQuery; query->base.free = FreeNearQuery; query->_operand = operand; query->_latitude = latitude; query->_longitude = longitude; query->_limit = -1; query->_distance = (distance == NULL ? NULL : TRI_DuplicateString(distance)); return &query->base; } //////////////////////////////////////////////////////////////////////////////// /// @brief skips elements of an existing query //////////////////////////////////////////////////////////////////////////////// TRI_fluent_query_t* TRI_CreateSelectFullQuery (TRI_fluent_query_t* operand, size_t n, TRI_shape_pid_t* pids, TRI_shaped_json_t** values) { TRI_select_full_query_t* query; query = TRI_Allocate(sizeof(TRI_select_full_query_t)); query->base._type = TRI_QUE_TYPE_SELECT_FULL_QUERY; query->base._collection = operand->_collection; query->base._isOptimised = false; query->base.optimise = OptimiseSelectFullQuery; query->base.execute = ExecuteSelectFullQuery; query->base.json = JsonSelectFullQuery; query->base.stringify = StringifySelectFullQuery; query->base.clone = CloneSelectFullQuery; query->base.free = FreeSelectFullQuery; query->_operand = operand; query->_length = n; query->_pids = pids; query->_values = values; return &query->base; } //////////////////////////////////////////////////////////////////////////////// /// @brief skips elements of an existing query //////////////////////////////////////////////////////////////////////////////// TRI_fluent_query_t* TRI_CreateSkipQuery (TRI_fluent_query_t* operand, TRI_voc_size_t skip) { TRI_skip_query_t* query; query = TRI_Allocate(sizeof(TRI_skip_query_t)); query->base._type = TRI_QUE_TYPE_SKIP; query->base._collection = operand->_collection; query->base._isOptimised = false; query->base.optimise = OptimiseSkipQuery; query->base.execute = ExecuteSkipQuery; query->base.json = JsonSkipQuery; query->base.stringify = StringifySkipQuery; query->base.clone = CloneSkipQuery; query->base.free = FreeSkipQuery; query->_operand = operand; query->_skip = skip; return &query->base; } //////////////////////////////////////////////////////////////////////////////// /// @brief looks up documents within a given radius //////////////////////////////////////////////////////////////////////////////// TRI_fluent_query_t* TRI_CreateWithinQuery (TRI_fluent_query_t* operand, double latitude, double longitude, double radius, char const* distance) { TRI_within_query_t* query; query = TRI_Allocate(sizeof(TRI_within_query_t)); query->base._type = TRI_QUE_TYPE_WITHIN; query->base._collection = operand->_collection; query->base._isOptimised = false; query->base.optimise = OptimiseWithinQuery; query->base.execute = ExecuteWithinQuery; query->base.json = JsonWithinQuery; query->base.stringify = StringifyWithinQuery; query->base.clone = CloneWithinQuery; query->base.free = FreeWithinQuery; query->_operand = operand; query->_latitude = latitude; query->_longitude = longitude; query->_radius = radius; query->_distance = (distance == NULL ? NULL : TRI_DuplicateString(distance)); return &query->base; } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- public functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup VocBase /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief executes a query //////////////////////////////////////////////////////////////////////////////// TRI_result_set_t* TRI_ExecuteQuery (TRI_fluent_query_t* query) { TRI_doc_collection_t* collection = query->_collection->_collection; TRI_rs_container_element_t* ce; TRI_fluent_query_result_t result; TRI_result_set_t* rs; TRI_rs_info_t info; result._cursor = TRI_DuplicateString("query"); result._error = NULL; result._length = 0; result._total = 0; result._documents = NULL; result._augmented = NULL; result._scannedIndexEntries = 0; result._scannedDocuments = 0; result._matchedDocuments = 0; info._runtime = -TRI_microtime(); // ............................................................................. // inside a read transaction // ............................................................................. collection->beginRead(collection); ce = TRI_AddResultSetRSContainer(&collection->_resultSets); query->execute(query, &result); if (result._error) { LOG_TRACE("query returned error: %s", result._error); rs = TRI_CreateRSSingle(collection, ce, NULL, 0); rs->_error = result._error; FreeAugmented(&result); } else { LOG_TRACE("query returned '%lu' documents", (unsigned long) result._length); rs = TRI_CreateRSVector(collection, ce, result._documents, result._augmented, result._length, result._total); } ce->_resultSet = rs; collection->endRead(collection); // ............................................................................. // outside a read transaction // ............................................................................. info._cursor = result._cursor; info._runtime += TRI_microtime(); info._scannedIndexEntries = result._scannedIndexEntries; info._scannedDocuments = result._scannedDocuments; info._matchedDocuments = result._matchedDocuments; rs->_info = info; FreeDocuments(&result); return rs; } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // Local Variables: // mode: outline-minor // outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)" // End: