├── README.md ├── docs └── api │ └── sonic.html ├── sonic.nimble └── src └── sonic.nim /README.md: -------------------------------------------------------------------------------- 1 | # nim-sonic-client 2 | 3 | 4 | nim client for [sonic](https://github.com/valeriansaliou/sonic) search backend. 5 | 6 | ## Install 7 | 8 | ``` 9 | nimble install sonic 10 | ``` 11 | 12 | ## Examples 13 | 14 | 15 | ### Ingest 16 | ```nim 17 | var cl = open("127.0.0.1", 1491, "dmdm", SonicChannel.Ingest) 18 | echo $cl.execCommand("PING") 19 | 20 | echo cl.ping() 21 | echo cl.protocol 22 | echo cl.bufsize 23 | echo cl.push("wiki", "articles", "article-1", 24 | "for the love of god hell") 25 | echo cl.push("wiki", "articles", "article-2", 26 | "for the love of satan heaven") 27 | echo cl.push("wiki", "articles", "article-3", 28 | "for the love of lorde hello") 29 | echo cl.push("wiki", "articles", "article-4", 30 | "for the god of loaf helmet") 31 | ``` 32 | ``` 33 | PONG 34 | true 35 | 0 36 | 0 37 | true 38 | 2 39 | 0 40 | true 41 | true 42 | true 43 | ``` 44 | 45 | 46 | ### Search 47 | ```nim 48 | 49 | var cl = open("127.0.0.1", 1491, "dmdm", SonicChannel.Search) 50 | echo $cl.execCommand("PING") 51 | 52 | echo cl.ping() 53 | echo cl.query("wiki", "articles", "for") 54 | echo cl.query("wiki", "articles", "love") 55 | echo cl.suggest("wiki", "articles", "hell") 56 | echo cl.suggest("wiki", "articles", "lo") 57 | ``` 58 | ``` 59 | PONG 60 | true 61 | @[] 62 | @["article-3", "article-2"] 63 | @[] 64 | @["loaf", "lorde", "love"] 65 | 66 | ``` 67 | ### Control 68 | ```nim 69 | var cl = open("127.0.0.1", 1491, "dmdm", SonicChannel.Control) 70 | echo $cl.execCommand("PING") 71 | 72 | echo cl.ping() 73 | echo cl.trigger("consolidate") 74 | ``` 75 | ``` 76 | PONG 77 | true 78 | OK 79 | ``` 80 | ## API reference 81 | 82 | API documentation can be found at [docs/api](./docs/api/sonic.html) and also [Browsable](https://xmonader.github.io/nim-sonic-client/api/sonic.html) 83 | 84 | ### Generating docs 85 | use `nimble genDocs` 86 | -------------------------------------------------------------------------------- /docs/api/sonic.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | sonic 20 | 1201 | 1202 | 1203 | 1204 | 1217 | 1218 | 1219 | 1220 |
1221 |
1222 |

sonic

1223 |
1224 |
1225 | 1229 |
1230 | Search: 1232 |
1233 |
1234 | Group by: 1235 | 1239 |
1240 | 1340 | 1341 |
1342 |
1343 |
1344 |

Sonicclient Copyright Ahmed T. Youssef nim Sonic client TODO: check help.

1345 |
1346 |

Types

1347 |
1348 | 1349 |
SonicChannel {...}{.pure.} = enum
1350 |   Ingest, Search, Control
1351 |
1352 | 1353 | 1354 |
1355 | 1356 |
Sonic = ref object of SonicBase[Socket]
1357 |
1358 | 1359 | 1360 |
1361 | 1362 |
AsyncSonic = ref object of SonicBase[AsyncSocket]
1363 |
1364 | 1365 | 1366 |
1367 | 1368 |
1369 |
1370 |

Procs

1371 |
1372 | 1373 |
proc startSession(this: AsyncSonic): Future[void] {...}{.raises: [FutureError],
1374 |     tags: [RootEffect].}
1375 |
1376 | 1377 | 1378 |
1379 | 1380 |
proc startSession(this: Sonic): void {...}{.raises: [TimeoutError, OSError, SslError], tags: [
1381 |     ReadIOEffect, TimeEffect, WriteIOEffect].}
1382 |
1383 | ## started. FIXME extract protocol bufsize 1384 | 1385 |
1386 | 1387 |
proc open(host = "localhost"; port = 1491; password = ""; channel: SonicChannel;
1388 |          ssl = false; timeout = 0): Sonic {...}{.raises: [OSError, SslError, TimeoutError], tags: [
1389 |     ReadIOEffect, TimeEffect, WriteIOEffect].}
1390 |
1391 | 1392 | 1393 |
1394 | 1395 |
proc openAsync(host = "localhost"; port = 1491; password = ""; channel: SonicChannel;
1396 |               ssl = false; timeout = 0): Future[AsyncSonic] {...}{.raises: [FutureError],
1397 |     tags: [RootEffect].}
1398 |
1399 | Open an asynchronous connection to a Sonic server. 1400 | 1401 |
1402 | 1403 |
proc receiveManaged(this: AsyncSonic; size = 1): Future[string] {...}{.
1404 |     raises: [FutureError], tags: [RootEffect].}
1405 |
1406 | 1407 | 1408 |
1409 | 1410 |
proc receiveManaged(this: Sonic; size = 1): string {...}{.
1411 |     raises: [TimeoutError, OSError, SslError, SonicServerError],
1412 |     tags: [ReadIOEffect, TimeEffect].}
1413 |
1414 | 1415 | 1416 |
1417 | 1418 |
proc execCommand(this: AsyncSonic; command: string; args: seq[string]): Future[string] {...}{.
1419 |     raises: [FutureError], tags: [RootEffect].}
1420 |
1421 | 1422 | 1423 |
1424 | 1425 |
proc execCommand(this: Sonic; command: string; args: seq[string]): string {...}{.
1426 |     raises: [SslError, OSError, TimeoutError, SonicServerError],
1427 |     tags: [WriteIOEffect, ReadIOEffect, TimeEffect].}
1428 |
1429 | 1430 | 1431 |
1432 | 1433 |
proc execCommand(this: AsyncSonic; command: string): Future[string] {...}{.
1434 |     raises: [FutureError], tags: [RootEffect].}
1435 |
1436 | 1437 | 1438 |
1439 | 1440 |
proc execCommand(this: Sonic; command: string): string {...}{.
1441 |     raises: [SslError, OSError, TimeoutError, SonicServerError],
1442 |     tags: [WriteIOEffect, ReadIOEffect, TimeEffect].}
1443 |
1444 | 1445 | 1446 |
1447 | 1448 |
proc ping(this: AsyncSonic): Future[bool] {...}{.raises: [FutureError], tags: [RootEffect].}
1449 |
1450 | Send ping command to the server Returns: bool True if successfully reaching the server. 1451 | 1452 |
1453 | 1454 |
proc ping(this: Sonic): bool {...}{.raises: [SslError, OSError, TimeoutError,
1455 |                                    SonicServerError],
1456 |                            tags: [WriteIOEffect, ReadIOEffect, TimeEffect].}
1457 |
1458 | Send ping command to the server Returns: bool True if successfully reaching the server. 1459 | 1460 |
1461 | 1462 |
proc quit(this: AsyncSonic): Future[string] {...}{.raises: [FutureError],
1463 |     tags: [RootEffect].}
1464 |
1465 | Quit the channel and closes the connection. 1466 | 1467 |
1468 | 1469 |
proc quit(this: Sonic): string {...}{.raises: [SslError, OSError, TimeoutError,
1470 |                                      SonicServerError],
1471 |                              tags: [WriteIOEffect, ReadIOEffect, TimeEffect].}
1472 |
1473 | Quit the channel and closes the connection. 1474 | 1475 |
1476 | 1477 |
proc help(this: AsyncSonic; arg: string): Future[string] {...}{.raises: [FutureError],
1478 |     tags: [RootEffect].}
1479 |
1480 | Sends Help query. 1481 | 1482 |
1483 | 1484 |
proc help(this: Sonic; arg: string): string {...}{.raises: [SslError, OSError, TimeoutError,
1485 |     SonicServerError], tags: [WriteIOEffect, ReadIOEffect, TimeEffect].}
1486 |
1487 | Sends Help query. 1488 | 1489 |
1490 | 1491 |
proc push(this: AsyncSonic; collection, bucket, objectName, text: string; lang = ""): Future[
1492 |     bool] {...}{.raises: [FutureError], tags: [RootEffect].}
1493 |
1494 |
Push search data in the index
1495 |
  • collection: index collection (ie. what you search in, eg. messages, products, etc.)
  • 1496 |
  • bucket: index bucket name (ie. user-specific search classifier in the collection if you have any eg. user-1, user-2, .., otherwise use a common bucket name eg. generic, procault, common, ..)
  • 1497 |
  • objectName: object identifier that refers to an entity in an external database, where the searched object is stored (eg. you use Sonic to index CRM contacts by name; full CRM contact data is stored in a MySQL database; in this case the object identifier in Sonic will be the MySQL primary key for the CRM contact)
  • 1498 |
  • text: search text to be indexed can be a single word, or a longer text; within maximum length safety limits
  • 1499 |
  • lang: ISO language code
  • 1500 |
1501 |
Returns:
1502 |
bool True if search data are pushed in the index.
1503 |
1504 |
1505 |
1506 | 1507 | 1508 |
1509 | 1510 |
proc push(this: Sonic; collection, bucket, objectName, text: string; lang = ""): bool {...}{.
1511 |     raises: [SslError, OSError, TimeoutError, SonicServerError],
1512 |     tags: [WriteIOEffect, ReadIOEffect, TimeEffect].}
1513 |
1514 |
Push search data in the index
1515 |
  • collection: index collection (ie. what you search in, eg. messages, products, etc.)
  • 1516 |
  • bucket: index bucket name (ie. user-specific search classifier in the collection if you have any eg. user-1, user-2, .., otherwise use a common bucket name eg. generic, procault, common, ..)
  • 1517 |
  • objectName: object identifier that refers to an entity in an external database, where the searched object is stored (eg. you use Sonic to index CRM contacts by name; full CRM contact data is stored in a MySQL database; in this case the object identifier in Sonic will be the MySQL primary key for the CRM contact)
  • 1518 |
  • text: search text to be indexed can be a single word, or a longer text; within maximum length safety limits
  • 1519 |
  • lang: ISO language code
  • 1520 |
1521 |
Returns:
1522 |
bool True if search data are pushed in the index.
1523 |
1524 |
1525 |
1526 | 1527 | 1528 |
1529 | 1530 |
proc pop(this: AsyncSonic; collection, bucket, objectName, text: string): Future[int] {...}{.
1531 |     raises: [FutureError], tags: [RootEffect].}
1532 |
1533 |
Pop search data from the index
1534 |
  • collection: index collection (ie. what you search in, eg. messages, products, etc.)
  • 1535 |
  • bucket: index bucket name (ie. user-specific search classifier in the collection if you have any eg. user-1, user-2, .., otherwise use a common bucket name eg. generic, procault, common, ..)
  • 1536 |
  • objectName: object identifier that refers to an entity in an external database, where the searched object is stored (eg. you use Sonic to index CRM contacts by name; full CRM contact data is stored in a MySQL database; in this case the object identifier in Sonic will be the MySQL primary key for the CRM contact)
  • 1537 |
  • text: search text to be indexed can be a single word, or a longer text; within maximum length safety limits
  • 1538 |
1539 |
Returns:
1540 |
int
1541 |
1542 |
1543 |
1544 | 1545 | 1546 |
1547 | 1548 |
proc pop(this: Sonic; collection, bucket, objectName, text: string): int {...}{.raises: [
1549 |     SslError, OSError, TimeoutError, SonicServerError, OverflowError, ValueError],
1550 |     tags: [WriteIOEffect, ReadIOEffect, TimeEffect].}
1551 |
1552 |
Pop search data from the index
1553 |
  • collection: index collection (ie. what you search in, eg. messages, products, etc.)
  • 1554 |
  • bucket: index bucket name (ie. user-specific search classifier in the collection if you have any eg. user-1, user-2, .., otherwise use a common bucket name eg. generic, procault, common, ..)
  • 1555 |
  • objectName: object identifier that refers to an entity in an external database, where the searched object is stored (eg. you use Sonic to index CRM contacts by name; full CRM contact data is stored in a MySQL database; in this case the object identifier in Sonic will be the MySQL primary key for the CRM contact)
  • 1556 |
  • text: search text to be indexed can be a single word, or a longer text; within maximum length safety limits
  • 1557 |
1558 |
Returns:
1559 |
int
1560 |
1561 |
1562 |
1563 | 1564 | 1565 |
1566 | 1567 |
proc count(this: AsyncSonic; collection, bucket, objectName: string): Future[int] {...}{.
1568 |     raises: [FutureError], tags: [RootEffect].}
1569 |
1570 |
Count indexed search data
1571 |
  • collection: index collection (ie. what you search in, eg. messages, products, etc.)
  • 1572 |
  • bucket: index bucket name (ie. user-specific search classifier in the collection if you have any eg. user-1, user-2, .., otherwise use a common bucket name eg. generic, procault, common, ..)
  • 1573 |
  • objectName: object identifier that refers to an entity in an external database, where the searched object is stored (eg. you use Sonic to index CRM contacts by name; full CRM contact data is stored in a MySQL database; in this case the object identifier in Sonic will be the MySQL primary key for the CRM contact)
  • 1574 |
1575 |
1576 |
1577 |

Returns: int count of index search data.

1578 | 1579 | 1580 |
1581 | 1582 |
proc count(this: Sonic; collection, bucket, objectName: string): int {...}{.raises: [
1583 |     OverflowError, ValueError, SslError, OSError, TimeoutError, SonicServerError],
1584 |     tags: [WriteIOEffect, ReadIOEffect, TimeEffect].}
1585 |
1586 |
Count indexed search data
1587 |
  • collection: index collection (ie. what you search in, eg. messages, products, etc.)
  • 1588 |
  • bucket: index bucket name (ie. user-specific search classifier in the collection if you have any eg. user-1, user-2, .., otherwise use a common bucket name eg. generic, procault, common, ..)
  • 1589 |
  • objectName: object identifier that refers to an entity in an external database, where the searched object is stored (eg. you use Sonic to index CRM contacts by name; full CRM contact data is stored in a MySQL database; in this case the object identifier in Sonic will be the MySQL primary key for the CRM contact)
  • 1590 |
1591 |
1592 |
1593 |

Returns: int count of index search data.

1594 | 1595 | 1596 |
1597 | 1598 |
proc flushCollection(this: AsyncSonic; collection: string): Future[int] {...}{.
1599 |     raises: [FutureError], tags: [RootEffect].}
1600 |
1601 |
Flush all indexed data from a collection
1602 |
  • collection index collection (ie. what you search in, eg. messages, products, etc.)
  • 1603 |
1604 |

Returns:
1605 |
int number of flushed data
1606 |
1607 |

1608 |
1609 |
1610 | 1611 | 1612 |
1613 | 1614 |
proc flushCollection(this: Sonic; collection: string): int {...}{.raises: [OverflowError,
1615 |     ValueError, SslError, OSError, TimeoutError, SonicServerError],
1616 |     tags: [WriteIOEffect, ReadIOEffect, TimeEffect].}
1617 |
1618 |
Flush all indexed data from a collection
1619 |
  • collection index collection (ie. what you search in, eg. messages, products, etc.)
  • 1620 |
1621 |

Returns:
1622 |
int number of flushed data
1623 |
1624 |

1625 |
1626 |
1627 | 1628 | 1629 |
1630 | 1631 |
proc flushBucket(this: AsyncSonic; collection, bucket: string): Future[int] {...}{.
1632 |     raises: [FutureError], tags: [RootEffect].}
1633 |
1634 |
Flush all indexed data from a bucket in a collection
1635 |
  • collection: index collection (ie. what you search in, eg. messages, products, etc.)
  • 1636 |
  • bucket: index bucket name (ie. user-specific search classifier in the collection if you have any eg. user-1, user-2, .., otherwise use a common bucket name eg. generic, procault, common, ..)
  • 1637 |
1638 |
Returns:
1639 |
int number of flushed data
1640 |
1641 |
1642 |
1643 | 1644 | 1645 |
1646 | 1647 |
proc flushBucket(this: Sonic; collection, bucket: string): int {...}{.raises: [OverflowError,
1648 |     ValueError, SslError, OSError, TimeoutError, SonicServerError],
1649 |     tags: [WriteIOEffect, ReadIOEffect, TimeEffect].}
1650 |
1651 |
Flush all indexed data from a bucket in a collection
1652 |
  • collection: index collection (ie. what you search in, eg. messages, products, etc.)
  • 1653 |
  • bucket: index bucket name (ie. user-specific search classifier in the collection if you have any eg. user-1, user-2, .., otherwise use a common bucket name eg. generic, procault, common, ..)
  • 1654 |
1655 |
Returns:
1656 |
int number of flushed data
1657 |
1658 |
1659 |
1660 | 1661 | 1662 |
1663 | 1664 |
proc flushObject(this: AsyncSonic; collection, bucket, objectName: string): Future[int] {...}{.
1665 |     raises: [FutureError], tags: [RootEffect].}
1666 |
1667 |
Flush all indexed data from an object in a bucket in collection
1668 |
  • collection: index collection (ie. what you search in, eg. messages, products, etc.)
  • 1669 |
  • bucket: index bucket name (ie. user-specific search classifier in the collection if you have any eg. user-1, user-2, .., otherwise use a common bucket name eg. generic, procault, common, ..)
  • 1670 |
  • objectName: object identifier that refers to an entity in an external database, where the searched object is stored (eg. you use Sonic to index CRM contacts by name; full CRM contact data is stored in a MySQL database; in this case the object identifier in Sonic will be the MySQL primary key for the CRM contact)
  • 1671 |
1672 |
Returns:
1673 |
int number of flushed data
1674 |
1675 |
1676 |
1677 | 1678 | 1679 |
1680 | 1681 |
proc flushObject(this: Sonic; collection, bucket, objectName: string): int {...}{.raises: [
1682 |     OverflowError, ValueError, SslError, OSError, TimeoutError, SonicServerError],
1683 |     tags: [WriteIOEffect, ReadIOEffect, TimeEffect].}
1684 |
1685 |
Flush all indexed data from an object in a bucket in collection
1686 |
  • collection: index collection (ie. what you search in, eg. messages, products, etc.)
  • 1687 |
  • bucket: index bucket name (ie. user-specific search classifier in the collection if you have any eg. user-1, user-2, .., otherwise use a common bucket name eg. generic, procault, common, ..)
  • 1688 |
  • objectName: object identifier that refers to an entity in an external database, where the searched object is stored (eg. you use Sonic to index CRM contacts by name; full CRM contact data is stored in a MySQL database; in this case the object identifier in Sonic will be the MySQL primary key for the CRM contact)
  • 1689 |
1690 |
Returns:
1691 |
int number of flushed data
1692 |
1693 |
1694 |
1695 | 1696 | 1697 |
1698 | 1699 |
proc flush(this: AsyncSonic; collection: string; bucket = ""; objectName = ""): Future[int] {...}{.
1700 |     raises: [FutureError], tags: [RootEffect].}
1701 |
1702 |
Flush indexed data in a collection, bucket, or in an object.
1703 |
  • collection: index collection (ie. what you search in, eg. messages, products, etc.)
  • 1704 |
  • bucket: index bucket name (ie. user-specific search classifier in the collection if you have any eg. user-1, user-2, .., otherwise use a common bucket name eg. generic, procault, common, ..)
  • 1705 |
  • objectName: object identifier that refers to an entity in an external database, where the searched object is stored (eg. you use Sonic to index CRM contacts by name; full CRM contact data is stored in a MySQL database; in this case the object identifier in Sonic will be the MySQL primary key for the CRM contact)
  • 1706 |
1707 |
Returns:
1708 |
int number of flushed data
1709 |
1710 |
1711 |
1712 | 1713 | 1714 |
1715 | 1716 |
proc flush(this: Sonic; collection: string; bucket = ""; objectName = ""): int {...}{.raises: [
1717 |     OverflowError, ValueError, SslError, OSError, TimeoutError, SonicServerError],
1718 |     tags: [WriteIOEffect, ReadIOEffect, TimeEffect].}
1719 |
1720 |
Flush indexed data in a collection, bucket, or in an object.
1721 |
  • collection: index collection (ie. what you search in, eg. messages, products, etc.)
  • 1722 |
  • bucket: index bucket name (ie. user-specific search classifier in the collection if you have any eg. user-1, user-2, .., otherwise use a common bucket name eg. generic, procault, common, ..)
  • 1723 |
  • objectName: object identifier that refers to an entity in an external database, where the searched object is stored (eg. you use Sonic to index CRM contacts by name; full CRM contact data is stored in a MySQL database; in this case the object identifier in Sonic will be the MySQL primary key for the CRM contact)
  • 1724 |
1725 |
Returns:
1726 |
int number of flushed data
1727 |
1728 |
1729 |
1730 | 1731 | 1732 |
1733 | 1734 |
proc query(this: AsyncSonic; collection, bucket, terms: string; limit = 10;
1735 |           offset: int = 0; lang = ""): Future[seq[string]] {...}{.raises: [FutureError],
1736 |     tags: [RootEffect].}
1737 |
1738 |
Query the database
1739 |
  • collection index collection (ie. what you search in, eg. messages, products, etc.)
  • 1740 |
  • bucket index bucket name (ie. user-specific search classifier in the collection if you have any eg. user-1, user-2, .., otherwise use a common bucket name eg. generic, procault, common, ..)
  • 1741 |
  • terms text for search terms
  • 1742 |
  • limit a positive integer number; set within allowed maximum & minimum limits
  • 1743 |
  • offset a positive integer number; set within allowed maximum & minimum limits
  • 1744 |
  • lang an ISO 639-3 locale code eg. eng for English (if set, the locale must be a valid ISO 639-3 code; if not set, the locale will be guessed from text).
  • 1745 |
1746 |
Returns:
1747 |
list list of objects ids.
1748 |
1749 |
1750 |
1751 | 1752 | 1753 |
1754 | 1755 |
proc query(this: Sonic; collection, bucket, terms: string; limit = 10; offset: int = 0;
1756 |           lang = ""): seq[string] {...}{.raises: [SslError, OSError, TimeoutError,
1757 |                                         SonicServerError],
1758 |                                 tags: [WriteIOEffect, ReadIOEffect, TimeEffect].}
1759 |
1760 |
Query the database
1761 |
  • collection index collection (ie. what you search in, eg. messages, products, etc.)
  • 1762 |
  • bucket index bucket name (ie. user-specific search classifier in the collection if you have any eg. user-1, user-2, .., otherwise use a common bucket name eg. generic, procault, common, ..)
  • 1763 |
  • terms text for search terms
  • 1764 |
  • limit a positive integer number; set within allowed maximum & minimum limits
  • 1765 |
  • offset a positive integer number; set within allowed maximum & minimum limits
  • 1766 |
  • lang an ISO 639-3 locale code eg. eng for English (if set, the locale must be a valid ISO 639-3 code; if not set, the locale will be guessed from text).
  • 1767 |
1768 |
Returns:
1769 |
list list of objects ids.
1770 |
1771 |
1772 |
1773 | 1774 | 1775 |
1776 | 1777 |
proc suggest(this: AsyncSonic; collection, bucket, word: string; limit = 10): Future[
1778 |     seq[string]] {...}{.raises: [FutureError], tags: [RootEffect].}
1779 |
1780 |
auto-completes word.
1781 |
  • collection index collection (ie. what you search in, eg. messages, products, etc.)
  • 1782 |
  • bucket index bucket name (ie. user-specific search classifier in the collection if you have any eg. user-1, user-2, .., otherwise use a common bucket name eg. generic, procault, common, ..)
  • 1783 |
  • word word to autocomplete
  • 1784 |
  • limit a positive integer number; set within allowed maximum & minimum limits (procault: {None})
  • 1785 |
1786 |
Returns:
1787 |
list list of suggested words.
1788 |
1789 |
1790 |
1791 | 1792 | 1793 |
1794 | 1795 |
proc suggest(this: Sonic; collection, bucket, word: string; limit = 10): seq[string] {...}{.
1796 |     raises: [SslError, OSError, TimeoutError, SonicServerError],
1797 |     tags: [WriteIOEffect, ReadIOEffect, TimeEffect].}
1798 |
1799 |
auto-completes word.
1800 |
  • collection index collection (ie. what you search in, eg. messages, products, etc.)
  • 1801 |
  • bucket index bucket name (ie. user-specific search classifier in the collection if you have any eg. user-1, user-2, .., otherwise use a common bucket name eg. generic, procault, common, ..)
  • 1802 |
  • word word to autocomplete
  • 1803 |
  • limit a positive integer number; set within allowed maximum & minimum limits (procault: {None})
  • 1804 |
1805 |
Returns:
1806 |
list list of suggested words.
1807 |
1808 |
1809 |
1810 | 1811 | 1812 |
1813 | 1814 |
proc trigger(this: AsyncSonic; action = ""): Future[string] {...}{.raises: [FutureError],
1815 |     tags: [RootEffect].}
1816 |
1817 |
Trigger an action
1818 |
action text for action
1819 |
1820 | 1821 | 1822 |
1823 | 1824 |
proc trigger(this: Sonic; action = ""): string {...}{.
1825 |     raises: [SslError, OSError, TimeoutError, SonicServerError],
1826 |     tags: [WriteIOEffect, ReadIOEffect, TimeEffect].}
1827 |
1828 |
Trigger an action
1829 |
action text for action
1830 |
1831 | 1832 | 1833 |
1834 | 1835 |
1836 | 1837 |
1838 |
1839 | 1840 |
1841 | 1846 |
1847 |
1848 |
1849 | 1850 | 1851 | 1852 | -------------------------------------------------------------------------------- /sonic.nimble: -------------------------------------------------------------------------------- 1 | # Package 2 | 3 | version = "0.1.0" 4 | author = "xmonader" 5 | description = "client for sonic search backend" 6 | license = "MIT" 7 | srcDir = "src" 8 | 9 | 10 | # Dependencies 11 | 12 | requires "nim >= 0.19.4" 13 | 14 | task genDocs, "Create code documentation for sonic": 15 | exec "nim doc --threads:on --project src/sonic.nim && rm -rf docs/api; mkdir -p docs && mv src/htmldocs docs/api " -------------------------------------------------------------------------------- /src/sonic.nim: -------------------------------------------------------------------------------- 1 | ## Sonicclient 2 | ## Copyright Ahmed T. Youssef 3 | ## nim Sonic client 4 | import strformat, tables, json, strutils, sequtils, hashes, net, asyncdispatch, asyncnet, os, strutils, parseutils, deques, options, net 5 | 6 | type 7 | SonicChannel* {.pure.} = enum 8 | Ingest 9 | Search 10 | Control 11 | 12 | type 13 | SonicBase[TSocket] = ref object of RootObj 14 | socket: TSocket 15 | host: string 16 | port: int 17 | password: string 18 | connected: bool 19 | timeout*: int 20 | protocol*: int 21 | bufSize*: int 22 | channel*: SonicChannel 23 | 24 | Sonic* = ref object of SonicBase[net.Socket] 25 | AsyncSonic* = ref object of SonicBase[asyncnet.AsyncSocket] 26 | 27 | type 28 | SonicServerError = object of Exception 29 | 30 | when defined(ssl): 31 | proc SSLifySonicConnectionNoVerify(Sonic: var Sonic|AsyncSonic) = 32 | let ctx = newContext(verifyMode=CVerifyNone) 33 | ctx.wrapSocket(Sonic.socket) 34 | 35 | proc quoteText(text:string): string = 36 | ## Quote text and normalize it in sonic protocol context. 37 | ## - text str text to quote/escape 38 | ## Returns: 39 | ## str quoted text 40 | 41 | return '"' & text.replace('"', '\"').replace("\r\n", "") & '"' 42 | 43 | 44 | proc isError(response: string): bool = 45 | ## Check if the response is Error or not in sonic context. 46 | ## Errors start with `ERR` 47 | ## - response response string 48 | ## Returns: 49 | ## bool true if response is an error. 50 | 51 | response.startsWith("ERR ") 52 | 53 | 54 | proc raiseForError(response:string): string = 55 | ## Raise SonicServerError in case of error response. 56 | ## - response message to check if it's error or not. 57 | ## Returns: 58 | ## str the response message 59 | if isError(response): 60 | raise newException(SonicServerError, response) 61 | return response 62 | 63 | 64 | proc startSession*(this:Sonic|AsyncSonic): Future[void] {.multisync.} = 65 | let resp = await this.socket.recvLine() 66 | 67 | if "CONNECTED" in resp: 68 | this.connected = true 69 | 70 | var channelName = "" 71 | case this.channel: 72 | of SonicChannel.Ingest: channelName = "ingest" 73 | of SonicChannel.Search: channelName = "search" 74 | of SonicChannel.COntrol: channelName = "control" 75 | 76 | let msg = fmt"START {channelName} {this.password} \r\n" 77 | await this.socket.send(msg) #### start 78 | discard await this.socket.recvLine() #### started. FIXME extract protocol bufsize 79 | 80 | proc open*(host = "localhost", port = 1491, password="", channel:SonicChannel, ssl=false, timeout=0): Sonic = 81 | result = Sonic( 82 | socket: newSocket(buffered = true), 83 | host: host, 84 | port: port, 85 | password: password, 86 | channel: channel 87 | ) 88 | result.timeout = timeout 89 | result.channel = channel 90 | when defined(ssl): 91 | if ssl == true: 92 | SSLifySonicConnectionNoVerify(result) 93 | result.socket.connect(host, port.Port) 94 | 95 | result.startSession() 96 | 97 | proc openAsync*(host = "localhost", port = 1491, password="", channel:SonicChannel, ssl=false, timeout=0): Future[AsyncSonic] {.async.} = 98 | ## Open an asynchronous connection to a Sonic server. 99 | result = AsyncSonic( 100 | socket: newAsyncSocket(buffered = true), 101 | channel: channel 102 | ) 103 | when defined(ssl): 104 | if ssl == true: 105 | SSLifySonicConnectionNoVerify(result) 106 | result.timeout = timeout 107 | await result.socket.connect(host, port.Port) 108 | await result.startSession() 109 | 110 | proc receiveManaged*(this:Sonic|AsyncSonic, size=1): Future[string] {.multisync.} = 111 | when this is Sonic: 112 | if this.timeout == 0: 113 | result = this.socket.recvLine() 114 | else: 115 | result = this.socket.recvLine(timeout=this.timeout) 116 | else: 117 | result = await this.socket.recvLine() 118 | 119 | result = raiseForError(result.strip()) 120 | 121 | proc execCommand*(this: Sonic|AsyncSonic, command: string, args:seq[string]): Future[string] {.multisync.} = 122 | let cmdArgs = concat(@[command], args) 123 | let cmdStr = join(cmdArgs, " ").strip() 124 | await this.socket.send(cmdStr & "\r\n") 125 | result = await this.receiveManaged() 126 | 127 | proc execCommand*(this: Sonic|AsyncSonic, command: string): Future[string] {.multisync.} = 128 | result = await this.execCommand(command, @[""]) 129 | 130 | proc ping*(this: Sonic|AsyncSonic): Future[bool] {.multisync.} = 131 | ## Send ping command to the server 132 | ## Returns: 133 | ## bool True if successfully reaching the server. 134 | result = (await this.execCommand("PING")) == "PONG" 135 | 136 | proc quit*(this: Sonic|AsyncSonic): Future[string] {.multisync.} = 137 | ## Quit the channel and closes the connection. 138 | result = await this.execCommand("QUIT") 139 | this.socket.close() 140 | 141 | ## TODO: check help. 142 | proc help*(this: Sonic|AsyncSonic, arg: string): Future[string] {.multisync.} = 143 | ## Sends Help query. 144 | result = await this.execCommand("HELP", @[arg]) 145 | 146 | proc push*(this: Sonic|AsyncSonic, collection, bucket, objectName, text: string, lang=""): Future[bool] {.multisync.} = 147 | ## Push search data in the index 148 | ## - collection: index collection (ie. what you search in, eg. messages, products, etc.) 149 | ## - bucket: index bucket name (ie. user-specific search classifier in the collection if you have any eg. user-1, user-2, .., otherwise use a common bucket name eg. generic, procault, common, ..) 150 | ## - objectName: object identifier that refers to an entity in an external database, where the searched object is stored (eg. you use Sonic to index CRM contacts by name; full CRM contact data is stored in a MySQL database; in this case the object identifier in Sonic will be the MySQL primary key for the CRM contact) 151 | ## - text: search text to be indexed can be a single word, or a longer text; within maximum length safety limits 152 | ## - lang: ISO language code 153 | ## Returns: 154 | ## bool True if search data are pushed in the index. 155 | var langString = "" 156 | if lang != "": 157 | langString = fmt"LANG({lang})" 158 | let text = quoteText(text) 159 | result = (await this.execCommand("PUSH", @[collection, bucket, objectName, text, langString]))=="OK" 160 | 161 | proc pop*(this: Sonic|AsyncSonic, collection, bucket, objectName, text: string): Future[int] {.multisync.} = 162 | ## Pop search data from the index 163 | ## - collection: index collection (ie. what you search in, eg. messages, products, etc.) 164 | ## - bucket: index bucket name (ie. user-specific search classifier in the collection if you have any eg. user-1, user-2, .., otherwise use a common bucket name eg. generic, procault, common, ..) 165 | ## - objectName: object identifier that refers to an entity in an external database, where the searched object is stored (eg. you use Sonic to index CRM contacts by name; full CRM contact data is stored in a MySQL database; in this case the object identifier in Sonic will be the MySQL primary key for the CRM contact) 166 | ## - text: search text to be indexed can be a single word, or a longer text; within maximum length safety limits 167 | ## Returns: 168 | ## int 169 | let text = quoteText(text) 170 | let resp = await this.execCommand("POP", @[collection, bucket, objectName, text]) 171 | result = resp.split()[^1].parseInt() 172 | 173 | proc count*(this: Sonic|AsyncSonic, collection, bucket, objectName: string): Future[int] {.multisync.} = 174 | ## Count indexed search data 175 | ## - collection: index collection (ie. what you search in, eg. messages, products, etc.) 176 | ## - bucket: index bucket name (ie. user-specific search classifier in the collection if you have any eg. user-1, user-2, .., otherwise use a common bucket name eg. generic, procault, common, ..) 177 | ## - objectName: object identifier that refers to an entity in an external database, where the searched object is stored (eg. you use Sonic to index CRM contacts by name; full CRM contact data is stored in a MySQL database; in this case the object identifier in Sonic will be the MySQL primary key for the CRM contact) 178 | ## Returns: 179 | ## int count of index search data. 180 | 181 | var bucketString = "" 182 | if bucket != "": 183 | bucketString = bucket 184 | var objectNameString = "" 185 | if objectName != "": 186 | objectNameString = objectName 187 | result = parseInt(await this.execCommand("COUNT", @[collection, bucket, objectName])) 188 | 189 | proc flushCollection*(this: Sonic|AsyncSonic, collection: string): Future[int] {.multisync.} = 190 | ## Flush all indexed data from a collection 191 | ## - collection index collection (ie. what you search in, eg. messages, products, etc.) 192 | ## Returns: 193 | ## int number of flushed data 194 | result = (await this.execCommand("FLUSHC", @[collection])).parseInt 195 | 196 | proc flushBucket*(this: Sonic|AsyncSonic, collection, bucket: string): Future[int] {.multisync.} = 197 | ## Flush all indexed data from a bucket in a collection 198 | ## - collection: index collection (ie. what you search in, eg. messages, products, etc.) 199 | ## - bucket: index bucket name (ie. user-specific search classifier in the collection if you have any eg. user-1, user-2, .., otherwise use a common bucket name eg. generic, procault, common, ..) 200 | ## Returns: 201 | ## int number of flushed data 202 | result = (await this.execCommand("FLUSHB", @[collection, bucket])).parseInt 203 | 204 | proc flushObject*(this: Sonic|AsyncSonic, collection, bucket, objectName: string): Future[int] {.multisync.} = 205 | ## Flush all indexed data from an object in a bucket in collection 206 | ## - collection: index collection (ie. what you search in, eg. messages, products, etc.) 207 | ## - bucket: index bucket name (ie. user-specific search classifier in the collection if you have any eg. user-1, user-2, .., otherwise use a common bucket name eg. generic, procault, common, ..) 208 | ## - objectName: object identifier that refers to an entity in an external database, where the searched object is stored (eg. you use Sonic to index CRM contacts by name; full CRM contact data is stored in a MySQL database; in this case the object identifier in Sonic will be the MySQL primary key for the CRM contact) 209 | ## Returns: 210 | ## int number of flushed data 211 | result = (await this.execCommand("FLUSHO", @[collection, bucket, objectName])).parseInt 212 | 213 | proc flush*(this: Sonic|AsyncSonic, collection: string, bucket="", objectName=""): Future[int] {.multisync.} = 214 | ## Flush indexed data in a collection, bucket, or in an object. 215 | ## - collection: index collection (ie. what you search in, eg. messages, products, etc.) 216 | ## - bucket: index bucket name (ie. user-specific search classifier in the collection if you have any eg. user-1, user-2, .., otherwise use a common bucket name eg. generic, procault, common, ..) 217 | ## - objectName: object identifier that refers to an entity in an external database, where the searched object is stored (eg. you use Sonic to index CRM contacts by name; full CRM contact data is stored in a MySQL database; in this case the object identifier in Sonic will be the MySQL primary key for the CRM contact) 218 | ## Returns: 219 | ## int number of flushed data 220 | if bucket == "" and objectName=="": 221 | result = await this.flushCollection(collection) 222 | elif bucket != "" and objectName == "": 223 | result = await this.flushBucket(collection, bucket) 224 | elif objectName != "" and bucket != "": 225 | result = await this.flushObject(collection, bucket, objectName) 226 | 227 | proc query*(this: Sonic|AsyncSonic, collection, bucket, terms: string, limit=10, offset: int=0, lang=""): Future[seq[string]] {.multisync.} = 228 | ## Query the database 229 | ## - collection index collection (ie. what you search in, eg. messages, products, etc.) 230 | ## - bucket index bucket name (ie. user-specific search classifier in the collection if you have any eg. user-1, user-2, .., otherwise use a common bucket name eg. generic, procault, common, ..) 231 | ## - terms text for search terms 232 | ## - limit a positive integer number; set within allowed maximum & minimum limits 233 | ## - offset a positive integer number; set within allowed maximum & minimum limits 234 | ## - lang an ISO 639-3 locale code eg. eng for English (if set, the locale must be a valid ISO 639-3 code; if not set, the locale will be guessed from text). 235 | ## Returns: 236 | ## list list of objects ids. 237 | let limitString = fmt"LIMIT({limit})" 238 | var langString = "" 239 | if lang != "": 240 | langString = fmt"LANG({lang})" 241 | let offsetString = fmt"OFFSET({offset})" 242 | 243 | let termsString = quoteText(terms) 244 | discard await this.execCommand("QUERY", @[collection, bucket, termsString, limitString, offsetString, langString]) 245 | let resp = await this.receiveManaged() 246 | result = resp.splitWhitespace()[3..^1] 247 | 248 | proc suggest*(this: Sonic|AsyncSonic, collection, bucket, word: string, limit=10): Future[seq[string]] {.multisync.} = 249 | ## auto-completes word. 250 | ## - collection index collection (ie. what you search in, eg. messages, products, etc.) 251 | ## - bucket index bucket name (ie. user-specific search classifier in the collection if you have any eg. user-1, user-2, .., otherwise use a common bucket name eg. generic, procault, common, ..) 252 | ## - word word to autocomplete 253 | ## - limit a positive integer number; set within allowed maximum & minimum limits (procault: {None}) 254 | ## Returns: 255 | ## list list of suggested words. 256 | var limitString = fmt"LIMIT({limit})" 257 | let wordString = quoteText(word) 258 | discard await this.execCommand("SUGGEST", @[collection, bucket, wordString, limitString]) 259 | let resp = await this.receiveManaged() 260 | result = resp.splitWhitespace()[3..^1] 261 | 262 | 263 | proc trigger*(this: Sonic|AsyncSonic, action=""): Future[string] {.multisync.} = 264 | ## Trigger an action 265 | ## action text for action 266 | result = await this.execCommand("TRIGGER", @[action]) 267 | 268 | when isMainModule: 269 | 270 | proc testIngest() = 271 | var cl = open("127.0.0.1", 1491, "dmdm", SonicChannel.Ingest) 272 | echo $cl.execCommand("PING") 273 | 274 | echo cl.ping() 275 | echo cl.protocol 276 | echo cl.bufsize 277 | echo cl.push("wiki", "articles", "article-1", 278 | "for the love of god hell") 279 | echo cl.pop("wiki", "articles", "article-1", 280 | "for the love of god hell") 281 | echo cl.pop("wikis", "articles", "article-1", 282 | "for the love of god hell") 283 | echo cl.push("wiki", "articles", "article-2", 284 | "for the love of satan heaven") 285 | echo cl.push("wiki", "articles", "article-3", 286 | "for the love of lorde hello") 287 | echo cl.push("wiki", "articles", "article-4", 288 | "for the god of loaf helmet") 289 | 290 | proc testSearch() = 291 | 292 | var cl = open("127.0.0.1", 1491, "dmdm", SonicChannel.Search) 293 | echo $cl.execCommand("PING") 294 | 295 | echo cl.ping() 296 | echo cl.query("wiki", "articles", "for") 297 | echo cl.query("wiki", "articles", "love") 298 | echo cl.suggest("wiki", "articles", "hell") 299 | echo cl.suggest("wiki", "articles", "lo") 300 | 301 | proc testControl() = 302 | var cl = open("127.0.0.1", 1491, "dmdm", SonicChannel.Control) 303 | echo $cl.execCommand("PING") 304 | 305 | echo cl.ping() 306 | echo cl.trigger("consolidate") 307 | 308 | 309 | testIngest() 310 | testSearch() 311 | testControl() 312 | --------------------------------------------------------------------------------