UNPKG

@sugarcube/plugin-elasticsearch

Version:

Use [Elasticsearch](https://www.elastic.co/products/elasticsearch) for SugarCube data.

256 lines (176 loc) 8.9 kB
# `@sugarcube/plugin-elasticsearch` Use [Elasticsearch](https://www.elastic.co/products/elasticsearch) for SugarCube data. ## Installation ```shell npm install --save @sugarcube/plugin-elasticsearch ``` ## Plugins ### `elastic_export` Store data of the current envelope in Elasticsearch. **Configuration Options:** - `elastic.host`: Set the hostname of the Elasticsearch server. Defaults to `localhost`. - `elastic.port`: Set the port of the Elasticsearch server. Defaults to `9200`. - `elastic.index`: Define the prefix to be used for index names. Defaults to `sugarcube`. - `elastic.omit_fields`: Omit those fields from being stored in Elasticsearch. Define multiple fields by separating them with a comma. - `elastic.mappings`: Supply a path to a JSON file that contains custom mapping definitions. Those custom mappings are merged into the default mappings, which can be found in [`./src/mappings.js`](./src/mappings.js). **Example:** ```shell sugarcube -Q ddg_search:Keith\ Johnstone -p ddg_search,elastic_export ``` To use custom mapping, write your mappings in a JSON file: ```json { "cities": {"type": "nested"} } ``` ```shell sugarcube -Q ddg_search:Keith\ Johnstone \ -p ddg_search,elastic_export \ --elastic.index dancers \ --elastic.mappings mappings.json ``` Indices are created the first time an export happens. In order to change the mappings of an existing index see [this](https://www.elastic.co/blog/changing-mapping-with-zero-downtime), [this](https://www.elastic.co/blog/reindex-is-coming) and [this](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-reindex.html). **Metrics:** - `total`: The total number of units exported. - `new`: The number of new units indexed. - `existing`: The number of existing units updated. - `fail`: The number of units that failed to export. ### `elastic_import` Search for units in Elasticsearch and import them into the pipeline. Search bodies can be defined in JSON files and imported using the `glob_pattern` query type or supplied directly using the `elastic_query` source type. All search bodies are regular Elasticsearch [request bodies](https://www.elastic.co/guide/en/elasticsearch/reference/6.2/search-request-body.html) and use the [Elasticsearch query DSL](https://www.elastic.co/guide/en/elasticsearch/reference/6.2/query-dsl.html). See [`queries.md`](./queries.md) for more examples. **Configuration Options:** - `elastic.host`: Set the hostname of the Elasticsearch server. Defaults to `localhost`. - `elastic.port`: Set the port of the Elasticsearch server. Defaults to `9200`. - `elastic.index`: Define the prefix to be used for index names. Defaults to `sugarcube`. - `elastic.amount`: Set the maximum numbers of units to import. Defaults to `1000`. - `elastic.include_fields`: Only fetch the fields specified. Defaults to fetch all fields. `_sc_id_hash` and `_sc_content_hash` are always fetched and don't need to be specified when using this option. **Example:** The following request body selects all units fetched in 2018. Place it in a file named `2018.json`. ```json { "query": { "range" : { "$sc_pubdates.fetch" : { "gte" : "2018-01-01", "lt" : "2018-12-31", "format": "YYYY-MM-dd" } } } } ``` To retrieve units based on this query and export them to a CSV file run the following pipeline: ```shell sugarcube -Q glob_pattern:2018.json -p elastic_import,csv_export ``` Let's say I have two types of lists, one are keywords, and the other one are city names. The following shell script iterates over two arrays, and calls a pipeline for every possible combination of keywords/cities: ```shell #!/bin/bash declare -a cities=("aberdeen" "sheffield"); declare -a keywords=("qlikview" "expedian"); for i in "${cities[@]}" do for j in "${keywords[@]}" do sugarcube -Q elastic_query:'{"query": {"bool": {"must": [{"match": {"href_text": "'"$i"'"}},{"match": {"href_text": "'"$j"'"}}]}}}' -p elastic_import --csv.filename $i-$j.csv --elastic.index my-index done done ``` I can then call this script like that: ```shell ./pipelines.sh ``` **Metrics:** - `total`: The total number of units imported. ### `elastic_complement` Complement data in the pipeline with existing data stored in Elasticsearch. This merges new data into existing data. **Configuration Options:** - `elastic.host`: Set the hostname of the Elasticsearch server. Defaults to `localhost`. - `elastic.port`: Set the port of the Elasticsearch server. Defaults to `9200`. - `elastic.index`: Define the prefix to be used for index names. Defaults to `sugarcube`. **Example:** Search DuckDuckGo and update it with date already stored in Elasticsearch before importing the updated version. ```shell sugarcube -Q ddg_search:Keith\ Johnstone \ -p ddg_search,elastic_complement,elastic_export ``` **Metrics:** - `new`: The number of new units that weren't complemented. - `existing`: The number of units that were complemented. ### `elastic_complement_left` Complement data in the pipeline with existing data stored in Elasticsearch. This merges existing data into new data. **Configuration Options:** - `elastic.host`: Set the hostname of the Elasticsearch server. Defaults to `localhost`. - `elastic.port`: Set the port of the Elasticsearch server. Defaults to `9200`. - `elastic.index`: Define the prefix to be used for index names. Defaults to `sugarcube`. **Example:** Search DuckDuckGo and update any data stored in Elasticsearch with the new data before exporting it again to Elasticsearch. ```shell sugarcube -Q ddg_search:Keith\ Johnstone \ -p ddg_search,elastic_complement_left,elastic_export ``` **Metrics:** - `new`: The number of new units that weren't complemented. - `existing`: The number of units that were complemented. ### `elastic_supplement` This is an alias for the `elastic_complement_left` plugin. ## Indexes This plugin will create all indexes with custom mappings when they don't yet exist. Using the `elastic.index` option it is possible to specify a prefix for the indexes scheme. The following indexes are created with custom mappings: - `<prefix>-web-searches`: Store all units that originate from a DuckDuckGo Or Google search. - `<prefix>-feed`: Store all units that originate from an Facebook or Twitter feed, a Twitter search or a YouTube channel. - `<prefix>-units`: Any unit that doesn't go into one of the above indexes is stored in this catchall index. ## API This module exports the abstraction layer that is used to communicate with Elasticsearch. It can be used to write custom scripts using the same API. ### `Elastic.Do` ```hs Do :: (G: Generator, {host: String, port: Number, mappings: {}}): [Array, Array] ``` The `Do` function creates a context, in which a full interaction with Elasticsearch takes place. It takes a generator function that forms the interaction context and an configuration object containing the host and port of the Elasticsearch server. Additionally it accepts an object containing custom mappings. The `Do` context returns a tuple containing any results and the history of the interaction with Elasticsearch. The generator function receives a configured API as it's argument. This API is valid within a single interaction context: ```js const [results, history] = await Elastic.Do(function* ({queryByIds}) { yield queryByIds("sugarcube", ["id1", "id2"]); }, {host: "localhost", port: 9200}); history.forEach(([k, meta]) => console.log(`${k}: ${JSON.stringify(meta)}.`)); // Do something with the results. ``` Every `Do` context receives the following API to Elasticsearch: #### `query` ```hs query :: (index: String, body: Object, amount: Number): Array ``` Search Elasticsearch using a request body in the format of the Elasticsearch Query DSL. ```js Elastic.Do(function* fetchTenDocuments({query}) { const body = { query: { href_text: "search me", }, }; yield query("sugarcube", body, 10); }); ``` `query` returns an array containing any fetched documents. #### `queryByIds` ```hs queryByIds :: (index: String, ids: Array): Array ``` Fetch documents by their ID. It returns an array of any document fetched from Elasticsearch. ```js Elastic.Do(function* fetchUnits({queryByIds}) { yield queryByIds("sugarcube", [1, 2, 3, 4]); }); ``` #### `bulk` ```hs bulk :: (index: String, ops: Object): Array ``` Run a bulk operation. The `ops` object contains all the units for the different bulk operations. Currently units can only be indexed. The `bulk` operation returns an array containing any errors that occured. ```js Elastic.Do(function* bulkIndex({bulk}) { const units = envelope.data; const errors = yield bulk("sugarcube", {index: units}); if (errors.length > 0) { // ... deal with errrors } }); ``` ## License [GPL3](./LICENSE) @ [Christo](christo@cryptodrunks.net)