Sonic Rust Alternative to Elastic
Sonic
Sonic wants to be the Redis of search engines.
Quoting from their github page:
Sonic is a fast, lightweight and schema-less search backend. It ingests search texts and identifier tuples, that can then be queried against in microseconds time.
Sonic can be used as a simple alternative to super-heavy and full-featured search backends such as Elasticsearch in some use-cases.
Sonic is an identifier index, rather than a document index; when queried, it returns IDs that can then be used to refer to the matched documents in an external database.
Indexing data
Search terms are stored in collections and organized in buckets. If you need to segregate searches by users you can create a bucket per user.
You want to store object identifiers in the object
field, you then have to retrieve the related entity from whatever persistence system the ID belongs to.
- bucket: e.g. default, user1,
- collection: e.g. Notes, messages
- object: Identifier e.g.
EDE8750E-3F3F-4A06-BD75-21C85494E18F
- text: Text to index and which we will search for
- options: Object, lang
Sonic -* buckets -* collections -* objects -* text
Node.js
"use strict";
const SonicChannelIngest = require('sonic-channel').Ingest;
const sonicChannelIngest = new SonicChannelIngest({
host : "::1",
port : 1491,
auth : "SecretPassword"
}).connect({
connected : function() {
// Success handler
console.info("Sonic Channel succeeded to connect to socket (ingest).");
console.info("Running example...");
setTimeout(async function() {
let rawUpdates = [
{"timestamp" : "2019-10-18T18:38:57.188131+00:00", "operation" : "INSERT", "schema" : "public", "table" : "location", "data" : {"id":"f23acdd5-7a15-11e8-9d13-0642b0acf810","uuid":"f23acdd5-7a15-11e8-9d13-0642b0acf810","name":"WE-US-12741-A","code":"SF26","description":"WE-US-12741-A -- 731 Sansome Street - SF - CA","geolocation_lng":"-122.402318","geolocation_lat":"37.797111","floorplan":null,"index":100,"parent":null,"createdAt":"2019-10-18T18:38:57+00:00","updatedAt":"2019-10-18T18:38:57+00:00"}},
{"timestamp" : "2019-10-18T18:38:57.23578+00:00", "operation" : "INSERT", "schema" : "public", "table" : "location", "data" : {"id":"b221390d-7baa-11e8-9d13-0642b0acf810","uuid":"b221390d-7baa-11e8-9d13-0642b0acf810","name":"WE-US-12741-A -- 3FL","code":"SF26-3FL","description":"WE-US-12741-A -- 731 Sansome Street - SF - CA -- 3FL","geolocation_lng":"-122.402318","geolocation_lat":"37.797111","floorplan":null,"index":200,"parent":"f23acdd5-7a15-11e8-9d13-0642b0acf810","createdAt":"2019-10-18T18:38:57+00:00","updatedAt":"2019-10-18T18:38:57+00:00"}},
{"timestamp" : "2019-10-18T18:38:57.341402+00:00", "operation" : "INSERT", "schema" : "public", "table" : "location", "data" : {"id":"5b00b9d6-5e4b-4f7b-ac6a-de43d5c4f0e7","uuid":"5b00b9d6-5e4b-4f7b-ac6a-de43d5c4f0e7","name":"WE-US-12741-A -- 1FL","code":"SF26-1FL","description":"WE-US-12741-A -- 731 Sansome Street - SF - CA -- 1FL","geolocation_lng":"-122.402318","geolocation_lat":"37.797111","floorplan":null,"index":200,"parent":"f23acdd5-7a15-11e8-9d13-0642b0acf810","createdAt":"2019-10-18T18:38:57+00:00","updatedAt":"2019-10-18T18:38:57+00:00"}},
{"timestamp" : "2019-10-17T23:05:50.385658+00:00", "operation" : "UPDATE", "schema" : "public", "table" : "device", "data" : {"id":"057ad39e-65c3-dd54-a64c-7c848a28677d","uuid":"057ad39e-65c3-dd54-a64c-7c848a28677d","assetTag":"BDD439E8743DF","deviceId":null,"name":"WW-SF17 37FL Stair 3 37-919 Reader","description":"WW-SF17 37FL Stair 3 37-919 Reader","status":"paired","location":"54e86b8b-3b4e-11e7-a99c-063c4950d72f","parent":null,"batch":null,"type":"d13f856f-7641-5b28-6d4b-f6e0ef0e9cc0","coordinates":null,"metadata":{"portalName":"WW-SF17 37FL Stair 3 37-919","portalKey":1289,"readerKey":21320,"readerName":"WW-SF17 37FL Stair 3 37-919 Reader"},"createdAt":"2019-10-17T22:36:14+00:00","updatedAt":"2019-10-17T22:36:54+00:00"}},
];
for(let update of rawUpdates) {
let message = {
collection: update.table,
bucket: 'default',
object: update.data.id
};
console.log('-----')
console.info("Sent: push", message.object);
message.text = update.data.name;
console.info("key", message.text);
if(message.text) {
await sonicChannelIngest.push(
message.collection,
message.bucket,
message.object,
message.text
);
}
message.text = update.data.code;
console.info("key", message.text);
if(message.text) {
await sonicChannelIngest.push(
message.collection,
message.bucket,
message.object,
message.text
);
}
message.text = update.data.description;
console.info("key", message.text);
if(message.text) {
await sonicChannelIngest.push(
message.collection,
message.bucket,
message.object,
message.text
);
}
console.info("Sent: count");
await sonicChannelIngest.count(
message.collection,
message.bucket,
message.object
).then(function(count) {
console.info("Count succeeded", count);
})
.catch(function(error) {
console.error("Count failed", error);
});
}
console.info("Hold on...");
setTimeout(function() {
// Test close
sonicChannelIngest.close()
.then(function(data) {
console.info("Close succeeded", data);
})
.catch(function(error) {
console.error("Close failed", error);
process.exit(1);
});
}, 4000);
}, 500);
},
disconnected : function() {
// Disconnected handler
console.error("Sonic Channel is now disconnected (ingest).");
console.info("Done running example.");
process.exit(0);
},
timeout : function() {
// Timeout handler
console.error("Sonic Channel connection timed out (ingest).");
},
retrying : function() {
// Retry handler
console.error("Trying to reconnect to Sonic Channel (ingest)...");
},
error : function(error) {
// Failure handler
console.error("Sonic Channel failed to connect to socket (ingest).", error);
}
});
process.stdin.resume();
"use strict";
const SonicChannelSearch = require('sonic-channel').Search;
const sonicChannelSearch = new SonicChannelSearch({
host : "::1",
port : 1491,
auth : "SecretPassword"
}).connect({
connected : function() {
// Success handler
console.info("Sonic Channel succeeded to connect to socket (search).");
console.info("Running example...");
setTimeout(function() {
// Test query
sonicChannelSearch.query(
"location", "default", "sansome",
{
limit : 20,
offset : 0
}
)
.then(function(data) {
console.info("Query #1 succeeded", data);
})
.catch(function(error) {
console.error("Query #1 failed", error);
});
console.info("Sent: query");
// Test suggest
sonicChannelSearch.suggest(
"location", "default", "Sansome"
)
.then(function(data) {
console.info("Suggest #1 succeeded", data);
})
.catch(function(error) {
console.error("Suggest #1 failed", error);
});
console.info("Sent: suggest");
console.info("Hold on...");
setTimeout(function() {
// Test close (#1)
sonicChannelSearch.close()
.then(function(data) {
console.info("Close #1 succeeded", data);
})
.catch(function(error) {
console.error("Close #1 failed", error);
});
console.info("Sent: close#1");
// Test close (#2)
sonicChannelSearch.close()
.then(function(data) {
console.info("Close #2 succeeded", data);
})
.catch(function(error) {
console.error("Close #2 failed", error);
});
console.info("Sent: close#2");
// Test query (after close)
sonicChannelSearch.query(
"messages", "default", "hello"
)
.then(function(data) {
console.info("Query #2 succeeded (this is not expected)", data);
process.exit(1);
})
.catch(function(error) {
console.error("Query #2 failed (this is expected)", error);
process.exit(0);
});
console.info("Sent: queryAfterClose");
}, 4000);
}, 500);
},
disconnected : function() {
// Disconnected handler
console.error("Sonic Channel is now disconnected (search).");
console.info("Done running example.");
},
timeout : function() {
// Timeout handler
console.error("Sonic Channel connection timed out (search).");
},
retrying : function() {
// Retry handler
console.error("Trying to reconnect to Sonic Channel (search)...");
},
error : function(error) {
// Failure handler
console.error("Sonic Channel failed to connect to socket (search).", error);
}
});
process.stdin.resume();