BxDolAIVectorStoreMariaDbStore.php 3.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. <?php defined('BX_DOL') or die('hack attempt');
  2. /**
  3. * Copyright (c) UNA, Inc - https://una.io
  4. * MIT License - https://opensource.org/licenses/MIT
  5. *
  6. * MariaDB vector search limited to one Studio store.
  7. * Rows from every store share one table per embedding width and are marked with sourceType.
  8. *
  9. * @defgroup UnaCore UNA Core
  10. * @{
  11. */
  12. class BxDolAIVectorStoreMariaDbStore extends NeuronAI\RAG\VectorStore\MariaDBVectorStore
  13. {
  14. public function __construct(
  15. PDO $oPdo,
  16. int $iTopK,
  17. protected string $sStoreId,
  18. ) {
  19. parent::__construct($oPdo, BxDolAIVectorStoreMariaDb::tableForDimension(1024), $iTopK);
  20. }
  21. public function addDocuments(array $documents): NeuronAI\RAG\VectorStore\VectorStoreInterface
  22. {
  23. $aGrouped = [];
  24. foreach ($documents as $oDocument) {
  25. if (!$oDocument instanceof NeuronAI\RAG\Document)
  26. continue;
  27. $sTable = BxDolAIVectorStoreMariaDb::tableForDimension(count($oDocument->getEmbedding()));
  28. $aGrouped[$sTable][] = $oDocument;
  29. }
  30. foreach ($aGrouped as $sTable => $aDocuments) {
  31. $this->tableName = $sTable;
  32. parent::addDocuments($aDocuments);
  33. }
  34. return $this;
  35. }
  36. public function deleteBy(string $sourceType, ?string $sourceName = null): NeuronAI\RAG\VectorStore\VectorStoreInterface
  37. {
  38. foreach (BxDolAIVectorStoreMariaDb::DIMENSIONS as $iDimension) {
  39. $this->tableName = BxDolAIVectorStoreMariaDb::tableForDimension($iDimension);
  40. parent::deleteBy($sourceType, $sourceName);
  41. }
  42. return $this;
  43. }
  44. public function similaritySearch(array $embedding): iterable
  45. {
  46. $this->tableName = BxDolAIVectorStoreMariaDb::tableForDimension(count($embedding));
  47. $oStatement = $this->pdo->prepare(sprintf(
  48. <<<'SQL'
  49. SELECT id, content, sourceType, sourceName, metadata,
  50. VEC_DISTANCE_COSINE(embedding, VEC_FromText(:embedding)) AS distance
  51. FROM %s
  52. WHERE sourceType = :storeId
  53. ORDER BY distance ASC
  54. LIMIT %d
  55. SQL,
  56. $this->tableName,
  57. $this->topK,
  58. ));
  59. $oStatement->execute([
  60. ':embedding' => json_encode($embedding),
  61. ':storeId' => $this->sStoreId,
  62. ]);
  63. $aDocuments = [];
  64. while ($aRow = $oStatement->fetch(PDO::FETCH_ASSOC)) {
  65. $oDocument = new NeuronAI\RAG\Document($aRow['content']);
  66. $oDocument->id = $aRow['id'];
  67. $oDocument->sourceType = $aRow['sourceType'];
  68. $oDocument->sourceName = $aRow['sourceName'];
  69. $oDocument->score = NeuronAI\RAG\VectorSimilarity::similarityFromDistance((float)$aRow['distance']);
  70. $aMetadata = json_decode($aRow['metadata'] ?? '{}', true);
  71. if (is_array($aMetadata)) {
  72. foreach ($aMetadata as $sKey => $mixedValue) {
  73. if (!in_array($sKey, ['content', 'sourceType', 'sourceName', 'score', 'embedding', 'id'], true))
  74. $oDocument->addMetadata($sKey, $mixedValue);
  75. }
  76. }
  77. $aDocuments[] = $oDocument;
  78. }
  79. return $aDocuments;
  80. }
  81. }
  82. /** @} */