Manticore's data types can be split into two categories: full-text fields and attributes.
Full-text fields:
- can be indexed with natural language processing algorithms, therefore can be searched for keywords
- cannot be used for sorting or grouping
- original document's content can be retrieved
- original document's content can be used for highlighting
Full-text fields are represented by the data type text
. All other data types are called "attributes".
Attributes are non-full-text values associated with each document that can be used to perform non-full-text filtering, sorting and grouping during a search.
It is often desired to process full-text search results based not only on matching document ID and its rank, but also on a number of other per-document values. For example, one might need to sort news search results by date and then relevance, or search through products within a specified price range, or limit a blog search to posts made by selected users, or group results by month. To do this efficiently, Manticore enables not only full-text fields, but also additional attributes to be added to each document. These attributes can be used to filter, sort, or group full-text matches, or to search only by attributes.
The attributes, unlike full-text fields, are not full-text indexed. They are stored in the table, but it is not possible to search them as full-text.
A good example for attributes would be a forum posts table. Assume that only the title and content fields need to be full-text searchable - but that sometimes it is also required to limit search to a certain author or a sub-forum (i.e., search only those rows that have some specific values of author_id or forum_id); or to sort matches by post_date column; or to group matching posts by month of the post_date and calculate per-group match counts.
- SQL
- JSON
- PHP
- Python
- Javascript
- Java
- C#
- config
CREATE TABLE forum(title text, content text, author_id int, forum_id int, post_date timestamp);
This example shows running a full-text query filtered by author_id
, forum_id
and sorted by post_date
.
- SQL
- JSON
- PHP
- Python
- javascript
- java
- C#
select * from forum where author_id=123 and forum_id in (1,3,7) order by post_date desc
Manticore supports two types of attribute storages:
- row-wise - traditional storage available in Manticore Search out of the box
- columnar - provided by Manticore Columnar Library
As can be understood from their names, they store data differently. The traditional row-wise storage:
- stores attributes uncompressed
- all attributes of the same document are stored in one row close to each other
- rows are stored one by one
- accessing attributes is basically done by just multiplying the row ID by the stride (length of a single vector) and getting the requested attribute from the calculated memory location. It gives very low random access latency.
- attributes have to be in memory to get acceptable performance, otherwise due to the row-wise nature of the storage Manticore may have to read from disk too much unneeded data which is in many cases suboptimal.
With the columnar storage:
- each attribute is stored independently of all other attributes in its separate "column"
- storage is split into blocks of 65536 entries
- the blocks are stored compressed. This often allows storing just a few distinct values instead of storing all of them like in the row-wise storage. High compression ratio allows reading from disk faster and makes the memory requirement much lower
- when data is indexed, storage scheme is selected for each block independently. For example, if all values in a block are the same, it gets "const" storage and only one value is stored for the whole block. If there are less than 256 unique values per block, it gets "table" storage and stores indexes to a table of values instead of the values themselves
- search in a block can be early rejected if it's clear the requested value is not present in the block.
The columnar storage was designed to handle large data volume that does not fit into RAM, so the recommendations are:
- if you have enough memory for all your attributes you will benefit from the row-wise storage
- otherwise, the columnar storage can still give you decent performance with a much lower memory footprint which will allow you to store much more documents in your table
The traditional row-wise storage is the default, so if you want everything to be stored in a row-wise fashion, you don't need to do anything when you create a table.
To enable the columnar storage you need to:
- specify
engine='columnar'
in CREATE TABLE to make all attributes of the table columnar. Then, if you want to keep a specific attribute row-wise, you need to addengine='rowwise'
when you declare it. For example:create table tbl(title text, type int, price float engine='rowwise') engine='columnar'
- specify
engine='columnar'
for a specific attribute inCREATE TABLE
to make it columnar. For example:create table tbl(title text, type int, price float engine='columnar');
or
create table tbl(title text, type int, price float engine='columnar') engine='rowwise';
- in the plain mode you need to list attributes you want to be columnar in columnar_attrs.
Below is the list of data types supported by Manticore Search:
The document identifier is a mandatory attribute, and document IDs must be unique 64-bit unsigned integers. Document IDs can be explicitly specified, but if not, they are still enabled. Document IDs cannot be updated. Note that when retrieving document IDs, they are treated as signed 64-bit integers, which means they may be negative. Use the UINT64() function to cast them to unsigned 64-bit integers if necessary.
- Explicit ID
- Implicit ID
When you create a table, you can specify ID explicitly, but no matter what data type you use, it will be always as said previously - a signed 64-bit integer.
CREATE TABLE tbl(id bigint, content text);
DESC tbl;
+---------+--------+----------------+
| Field | Type | Properties |
+---------+--------+----------------+
| id | bigint | |
| content | text | indexed stored |
+---------+--------+----------------+
2 rows in set (0.00 sec)
General syntax:
string|text [stored|attribute] [indexed]
Properties:
indexed
- full-text indexed (can be used in full-text queries)stored
- stored in a docstore (stored on disk, not in RAM, lazy read)attribute
- makes it a string attribute (can sort/group by it)
Specifying at least one property overrides all the default ones (see below), i.e., if you decide to use a custom combination of properties, you need to list all the properties you want.
No properties specified:
string
and text
are aliases, but if you don’t specify any properties, they by default mean different things:
- just
string
by default meansattribute
(see details below). - just
text
by default meansstored
+indexed
(see details below).
The text (just text
or text/string indexed
) data type forms the full-text part of the table. Text fields are indexed and can be searched for keywords.
Text is passed through an analyzer pipeline that converts the text to words, applies morphology transformations, etc. Eventually, a full-text table (a special data structure that enables quick searches for a keyword) gets built from that text.
Full-text fields can only be used in the MATCH()
clause and cannot be used for sorting or aggregation. Words are stored in an inverted index along with references to the fields they belong to and positions in the field. This allows searching a word inside each field and using advanced operators like proximity. By default, the original text of the fields is both indexed and stored in document storage. It means that the original text can be returned with the query results and used in search result highlighting.
- SQL
- JSON
- PHP
- Python
- javascript
- java
- C#
- config
CREATE TABLE products(title text);
This behavior can be overridden by explicitly specifying that the text is only indexed.
- SQL
- JSON
- PHP
- Python
- javascript
- java
- C#
- config
CREATE TABLE products(title text indexed);
Fields are named, and you can limit your searches to a single field (e.g. search through "title" only) or a subset of fields (e.g. "title" and "abstract" only). You can have up to 256 full-text fields.
- SQL
- JSON
- PHP
- Python
- javascript
- java
- C#
select * from products where match('@title first');
Unlike full-text fields, string attributes (just string
or string/text attribute
) are stored as they are received and cannot be used in full-text searches. Instead, they are returned in results, can be used in the WHERE
clause for comparison filtering or REGEX
, and can be used for sorting and aggregation. In general, it's not recommended to store large texts in string attributes, but use string attributes for metadata like names, titles, tags, keys.
If you want to also index the string attribute, you can specify both as string attribute indexed
. It will allow full-text searching and works as an attribute.
- SQL
- JSON
- PHP
- Python
- javascript
- java
- C#
- config
CREATE TABLE products(title text, keys string);
Manticore doesn't have a dedicated field type for binary data, but you can store it safely by using base64 encoding and the text stored
or string stored
field types (which are synonyms). If you don't encode the binary data, parts of it may get lost — for example, Manticore trims the end of a string if it encounters a null-byte.
Here is an example where we encode the ls
command using base64, store it in Manticore, and then decode it to verify that the MD5 checksum remains unchanged:
- Example
# md5sum /bin/ls
43d1b8a7ccda411118e2caba685f4329 /bin/ls
# encoded_data=`base64 -i /bin/ls `
# mysql -P9306 -h0 -e "drop table if exists test; create table test(data text stored); insert into test(data) values('$encoded_data')"
# mysql -P9306 -h0 -NB -e "select data from test" | base64 -d > /tmp/ls | md5sum
43d1b8a7ccda411118e2caba685f4329 -
Integer type allows storing 32 bit unsigned integer values.
- SQL
- JSON
- PHP
- Python
- javascript
- java
- C#
- config
CREATE TABLE products(title text, price int);
Integers can be stored in shorter sizes than 32-bit by specifying a bit count. For example, if we want to store a numeric value which we know is not going to be bigger than 8, the type can be defined as bit(3)
. Bitcount integers perform slower than the full-size ones, but they require less RAM. They are saved in 32-bit chunks, so in order to save space, they should be grouped at the end of attribute definitions (otherwise a bitcount integer between 2 full-size integers will occupy 32 bits as well).
- SQL
- JSON
- PHP
- Python
- javascript
- java
- C#
- config
CREATE TABLE products(title text, flags bit(3), tags bit(2) );
Big integers (bigint) are 64-bit wide signed integers.
- SQL
- JSON
- PHP
- Python
- javascript
- java
- C#
- config
CREATE TABLE products(title text, price bigint );
Declares a boolean attribute. It's equivalent to an integer attribute with bit count of 1.
- SQL
- JSON
- PHP
- Python
- javascript
- java
- C#
- config
CREATE TABLE products(title text, sold bool );
The timestamp type represents Unix timestamps, which are stored as 32-bit integers. Unlike basic integers, the timestamp type allows the use of time and date functions. Conversion from string values follows these rules:
- Numbers without delimiters, at least 10 characters long, are converted to timestamps as is.
%Y-%m-%dT%H:%M:%E*S%Z
%Y-%m-%d'T'%H:%M:%S%Z
%Y-%m-%dT%H:%M:%E*S
%Y-%m-%dT%H:%M:%s
%Y-%m-%dT%H:%M
%Y-%m-%dT%H
%Y-%m-%d
%Y-%m
%Y
The meanings of these conversion specifiers are detailed in the strptime manual, except for %E*S
, which stands for milliseconds.
Note that auto-conversion of timestamps is not supported in plain tables.
- SQL
- JSON
- PHP
- Python
- javascript
- java
- C#
- config
CREATE TABLE products(title text, date timestamp);
Real numbers are stored as 32-bit IEEE 754 single precision floats.
- SQL
- JSON
- PHP
- Python
- javascript
- java
- C#
- config
CREATE TABLE products(title text, coeff float);
Unlike integer types, comparing two floating-point numbers for equality is not recommended due to potential rounding errors. A more reliable approach is to use a near-equal comparison, by checking the absolute error margin.
- SQL
- JSON
- PHP
- Python
- javascript
- java
- C#
select abs(a-b)<=0.00001 from products
Another alternative, which can also be used to perform IN(attr,val1,val2,val3)
is to compare floats as integers by choosing a multiplier factor and convert the floats to integers in operations. The following example illustrates modifying IN(attr,2.0,2.5,3.5)
to work with integer values.
- SQL
- JSON
- PHP
- Python
- javascript
- java
- C#
select in(ceil(attr*100),200,250,350) from products
Float values in Manticore are displayed with precision to ensure they reflect the exact stored value. This approach was introduced to prevent precision loss, especially for cases like geographical coordinates, where rounding to 6 decimal places caused inaccuracies.
Now, Manticore first outputs a number with 6 digits, then parses and compares it to the original value. If they don't match, additional digits are added until they do.
For example, if a float value was inserted as 19.45
, Manticore will display it as 19.450001
to accurately represent the stored value.
- Example
insert into t(id, f) values(1, 19.45)
--------------
Query OK, 1 row affected (0.02 sec)
--------------
select * from t
--------------
+------+-----------+
| id | f |
+------+-----------+
| 1 | 19.450001 |
+------+-----------+
1 row in set (0.00 sec)
--- 1 out of 1 results in 0ms ---
This data type allows storing JSON objects, which is useful for storing schema-less data. However, it is not supported by columnar storage. However, it can be stored in traditional storage, as it's possible to combine both storage types in the same table.
- SQL
- JSON
- PHP
- Python
- javascript
- java
- C#
- config
CREATE TABLE products(title text, data json);
JSON properties can be used in most operations. There are also special functions such as ALL(), ANY(), GREATEST(), LEAST() and INDEXOF() that allow traversal of property arrays.
- SQL
- JSON
- PHP
- Python
- javascript
- java
- C#
select indexof(x>2 for x in data.intarray) from products
Text properties are treated the same as strings, so it's not possible to use them in full-text match expressions. However, string functions such as REGEX() can be used.
- SQL
- JSON
- PHP
- Python
- javascript
- java
- C#
select regex(data.name, 'est') as c from products where c>0
In the case of JSON properties, enforcing data type may be required for proper functionality in certain situations. For example, when working with float values, DOUBLE() must be used for proper sorting.
- SQL
- JSON
- PHP
- Python
- javascript
- java
- C#
select * from products order by double(data.myfloat) desc
Float vector attributes allow storing variable-length lists of floats. It's important to note that this concept differs from multi-valued attributes. Multi-valued attributes (MVAs) are essentially sets; they do not preserve value order, and duplicate values are not retained. In contrast, float vectors perform no additional processing on values during insertion.
Float vector attributes can be used in k-nearest neighbor searches; see KNN search.
Currently, float_vector
fields can only be utilized in KNN search within real-time tables and the data type is not supported in any other functions or expressions, nor is it supported in plain tables.
- SQL
- JSON
- PHP
- Python
- javascript
- java
- C#
- config
CREATE TABLE products(title text, image_vector float_vector);
Multi-value attributes allow storing variable-length lists of 32-bit unsigned integers. This can be useful for storing one-to-many numeric values, such as tags, product categories, and properties.
- SQL
- JSON
- PHP
- Python
- javascript
- java
- C#
- config
CREATE TABLE products(title text, product_codes multi);
It supports filtering and aggregation, but not sorting. Filtering can be done using a condition that requires at least one element to pass (using ANY()) or all elements (ALL()) to pass.
- SQL
- JSON
- PHP
- Python
- javascript
- java
- C#
select * from products where any(product_codes)=3
Information like least or greatest element and length of the list can be extracted. An example shows ordering by the least element of a multi-value attribute.
- SQL
- JSON
- PHP
- Python
- javascript
- java
- C#
select least(product_codes) l from products order by l asc
When grouping by a multi-value attribute, a document will contribute to as many groups as there are different values associated with that document. For instance, if a collection contains exactly one document having a 'product_codes' multi-value attribute with values 5, 7, and 11, grouping on 'product_codes' will produce 3 groups with COUNT(*)
equal to 1 and GROUPBY()
key values of 5, 7, and 11, respectively. Also, note that grouping by multi-value attributes may lead to duplicate documents in the result set because each document can participate in many groups.
- SQL
insert into products values ( 1, 'doc one', (5,7,11) );
select id, count(*), groupby() from products group by product_codes;
Query OK, 1 row affected (0.00 sec)
+------+----------+-----------+
| id | count(*) | groupby() |
+------+----------+-----------+
| 1 | 1 | 11 |
| 1 | 1 | 7 |
| 1 | 1 | 5 |
+------+----------+-----------+
3 rows in set (0.00 sec)
The order of the numbers inserted as values of multivalued attributes is not preserved. Values are stored internally as a sorted set.
- SQL
- JSON
- PHP
- Python
- javascript
- java
- C#
insert into product values (1,'first',(4,2,1,3));
select * from products;
Query OK, 1 row affected (0.00 sec)
+------+---------------+-------+
| id | product_codes | title |
+------+---------------+-------+
| 1 | 1,2,3,4 | first |
+------+---------------+-------+
1 row in set (0.01 sec)
A data type that allows storing variable-length lists of 64-bit signed integers. It has the same functionality as multi-value integer.
- SQL
- JSON
- PHP
- Python
- javascript
- java
- C#
- config
CREATE TABLE products(title text, values multi64);
When you use the columnar storage you can specify the following properties for the attributes.
By default, Manticore Columnar storage stores all attributes in a columnar fashion, as well as in a special docstore row by row. This enables fast execution of queries like SELECT * FROM ...
, especially when fetching a large number of records at once. However, if you are sure that you do not need it or wish to save disk space, you can disable it by specifying fast_fetch='0'
when creating a table or (if you are defining a table in a config) by using columnar_no_fast_fetch
as shown in the following example.
- RT mode
- Plain mode
create table t(a int, b int fast_fetch='0') engine='columnar'; desc t;
+-------+--------+---------------------+
| Field | Type | Properties |
+-------+--------+---------------------+
| id | bigint | columnar fast_fetch |
| a | uint | columnar fast_fetch |
| b | uint | columnar |
+-------+--------+---------------------+
3 rows in set (0.00 sec)
In Manticore Search, there are two ways to manage tables:
Real-time mode requires no table definition in the configuration file. However, the data_dir directive in the searchd
section is mandatory. Index files are stored inside the data_dir
.
Replication is only available in this mode.
You can use SQL commands such as CREATE TABLE
, ALTER TABLE
and DROP TABLE
to create and modify table schema, and to drop it. This mode is particularly useful for real-time and percolate tables.
Table names are converted to lowercase when created.
In this mode, you can specify the table schema in the configuration file. Manticore reads this schema on startup and creates the table if it doesn't exist yet. This mode is particularly useful for plain tables that use data from an external storage.
To drop a table, remove it from the configuration file or remove the path setting and send a HUP signal to the server or restart it.
Table names are case-sensitive in this mode.
All table types are supported in this mode.
Table type | RT mode | Plain mode |
---|---|---|
Real-time | supported | supported |
Plain | not supported | supported |
Percolate | supported | supported |
Distributed | supported | supported |
Template | not supported | supported |
A real-time table is a main type of table in Manticore. It lets you add, update, and delete documents, and you can see these changes right away. You can set up a real-time Table in a configuration file or use commands like CREATE
, UPDATE
, DELETE
, or ALTER
.
Internally a real-time table consists of one or more plain tables called chunks. There are two kinds of chunks:
- multiple disk chunks - these are saved on a disk and are structured like a plain table.
- single ram chunk - this is kept in memory and collects all changes.
The size of the RAM chunk is controlled by the rt_mem_limit setting. Once this limit is reached, the RAM chunk is transferred to disk as a disk chunk. If there are too many disk chunks, Manticore combines some of them to improve performance.
You can create a new real-time table in two ways: by using the CREATE TABLE
command or through the _mapping endpoint of the HTTP JSON API.
You can use this command via both SQL and HTTP protocols:
- SQL
- JSON
- PHP
- Python
- Javascript
- Java
- C#
- CONFIG
CREATE TABLE products(title text, price float) morphology='stem_en';
Query OK, 0 rows affected (0.00 sec)
NOTE: The
_mapping
API requires Manticore Buddy. If it doesn't work, make sure Buddy is installed.
Alternatively, you can create a new table via the _mapping
endpoint. This endpoint allows you to define an Elasticsearch-like table structure to be converted to a Manticore table.
The body of your request must have the following structure:
"properties"
{
"FIELD_NAME_1":
{
"type": "FIELD_TYPE_1"
},
"FIELD_NAME_2":
{
"type": "FIELD_TYPE_2"
},
...
"FIELD_NAME_N":
{
"type": "FIELD_TYPE_M"
}
}
When creating a table, Elasticsearch data types will be mapped to Manticore types according to the following rules:
- aggregate_metric => json
- binary => string
- boolean => bool
- byte => int
- completion => string
- date => timestamp
- date_nanos => bigint
- date_range => json
- dense_vector => json
- flattened => json
- flat_object => json
- float => float
- float_range => json
- geo_point => json
- geo_shape => json
- half_float => float
- histogram => json
- integer => int
- integer_range => json
- ip => string
- ip_range => json
- keyword => string
- knn_vector => float_vector
- long => bigint
- long_range => json
- match_only_text => text
- object => json
- point => json
- scaled_float => float
- search_as_you_type => text
- shape => json
- short => int
- text => text
- unsigned_long => int
- version => string
- JSON
POST /your_table_name/_mapping -d '
{
"properties": {
"price": {
"type": "float"
},
"title": {
"type": "text"
}
}
}
'
{
"total":0,
"error":"",
"warning":""
}
You can create a copy of a real-time table, with or without its data. Please note that if the table is large, copying it with data may take some time. Copying works in synchronous mode, but if the connection is dropped, it will continue in the background.
CREATE TABLE [IF NOT EXISTS] table_name LIKE old_table_name [WITH DATA]
NOTE: Copying a table requires Manticore Buddy. If it doesn't work, make sure Buddy is installed.
- SQL
- Example (WITH DATA)
create table products LIKE old_products;
- Add documents.
- Update attributes and full-text fields using the Update process.
- Delete documents.
- Empty the table.
- Change the schema online with the
ALTER
command, as explained in Change schema online. - Define the table in a configuration file, as detailed in Define table.
- Use the UUID feature for automatic ID provisioning.
- Ingest data using the indexer feature.
- Connect it to sources for easy indexing from external storage.
- Update the killlist_target, as it is automatically managed by the real-time table.
The following table outlines the different file extensions and their respective descriptions in a real-time table:
Extension | Description |
---|---|
.lock |
A lock file that ensures that only one process can access the table at a time. |
.ram |
The RAM chunk of the table, stored in memory and used as an accumulator of changes. |
.meta |
The headers of the real-time table that define its structure and settings. |
.*.sp* |
Disk chunks that are stored on disk with the same format as plain tables. They are created when the RAM chunk size exceeds the rt_mem_limit. |
For more information on the structure of disk chunks, refer to the plain table files structure.