|
1 | | -import weaviate, { WeaviateClient } from '..'; |
| 1 | +import weaviate, { Reference, WeaviateClient, WeaviateError, WeaviateObject } from '..'; |
2 | 2 |
|
3 | 3 | describe('the graphql journey', () => { |
4 | 4 | let client: WeaviateClient; |
@@ -1344,6 +1344,97 @@ describe('query cluster with consistency level', () => { |
1344 | 1344 | }); |
1345 | 1345 | }); |
1346 | 1346 |
|
| 1347 | +describe('query with group by', () => { |
| 1348 | + let client: WeaviateClient; |
| 1349 | + |
| 1350 | + beforeEach(() => { |
| 1351 | + client = weaviate.client({ |
| 1352 | + scheme: 'http', |
| 1353 | + host: 'localhost:8080', |
| 1354 | + }); |
| 1355 | + }); |
| 1356 | + |
| 1357 | + it('creates Document Passage schema classes', () => { |
| 1358 | + // this is just test setup, not part of what we want to test here |
| 1359 | + return setupGroupBy(client); |
| 1360 | + }); |
| 1361 | + |
| 1362 | + test('should return 3 groups', async () => { |
| 1363 | + interface GroupHit { |
| 1364 | + passageIds: string[]; |
| 1365 | + ofDocumentId: string; |
| 1366 | + } |
| 1367 | + const hits = 'hits{ofDocument{... on Document{_additional{id}}} _additional{id distance}}'; |
| 1368 | + const group = `group{id groupValue count maxDistance minDistance ${hits}}`; |
| 1369 | + const _additional = `_additional{${group}}`; |
| 1370 | + const expectedGroupHits1: GroupHit = { |
| 1371 | + passageIds: [ |
| 1372 | + '00000000-0000-0000-0000-000000000001', |
| 1373 | + '00000000-0000-0000-0000-000000000009', |
| 1374 | + '00000000-0000-0000-0000-000000000007', |
| 1375 | + '00000000-0000-0000-0000-000000000008', |
| 1376 | + '00000000-0000-0000-0000-000000000006', |
| 1377 | + '00000000-0000-0000-0000-000000000010', |
| 1378 | + '00000000-0000-0000-0000-000000000005', |
| 1379 | + '00000000-0000-0000-0000-000000000004', |
| 1380 | + '00000000-0000-0000-0000-000000000003', |
| 1381 | + '00000000-0000-0000-0000-000000000002', |
| 1382 | + ], |
| 1383 | + ofDocumentId: '00000000-0000-0000-0000-00000000000a', |
| 1384 | + }; |
| 1385 | + const expectedGroupHits2: GroupHit = { |
| 1386 | + passageIds: [ |
| 1387 | + '00000000-0000-0000-0000-000000000011', |
| 1388 | + '00000000-0000-0000-0000-000000000013', |
| 1389 | + '00000000-0000-0000-0000-000000000012', |
| 1390 | + '00000000-0000-0000-0000-000000000014', |
| 1391 | + ], |
| 1392 | + ofDocumentId: '00000000-0000-0000-0000-00000000000b', |
| 1393 | + }; |
| 1394 | + const expectedGroupHits: GroupHit[] = [expectedGroupHits1, expectedGroupHits2]; |
| 1395 | + |
| 1396 | + await client.graphql |
| 1397 | + .get() |
| 1398 | + .withClassName('Passage') |
| 1399 | + .withGroupBy({ path: ['ofDocument'], groups: 3, objectsPerGroup: 10 }) |
| 1400 | + .withNearObject({ id: '00000000-0000-0000-0000-000000000001' }) |
| 1401 | + .withFields(_additional) |
| 1402 | + .do() |
| 1403 | + .then((res: any) => { |
| 1404 | + expect(res.data.Get.Passage).toHaveLength(3); |
| 1405 | + expect(res.data.Get.Passage[0]._additional.group.hits).toHaveLength(10); |
| 1406 | + expect(res.data.Get.Passage[1]._additional.group.hits).toHaveLength(4); |
| 1407 | + expect(res.data.Get.Passage[2]._additional.group.hits).toHaveLength(6); |
| 1408 | + for (let i = 0; i < 3; i++) { |
| 1409 | + expect(res.data.Get.Passage[i]._additional.group).toBeDefined(); |
| 1410 | + expect(res.data.Get.Passage[i]._additional.group.minDistance).toBe( |
| 1411 | + res.data.Get.Passage[i]._additional.group.hits[0]._additional.distance |
| 1412 | + ); |
| 1413 | + expect(res.data.Get.Passage[i]._additional.group.maxDistance).toBe( |
| 1414 | + res.data.Get.Passage[i]._additional.group.hits[ |
| 1415 | + res.data.Get.Passage[i]._additional.group.hits.length - 1 |
| 1416 | + ]._additional.distance |
| 1417 | + ); |
| 1418 | + } |
| 1419 | + for (let i = 0; i < 2; i++) { |
| 1420 | + const expectedResults = expectedGroupHits[i]; |
| 1421 | + const hits = res.data.Get.Passage[i]._additional.group.hits; |
| 1422 | + for (let j = 0; j < hits.length; j++) { |
| 1423 | + expect(hits[j]._additional.id).toBe(expectedResults.passageIds[j]); |
| 1424 | + expect(hits[j].ofDocument[0]._additional.id).toBe(expectedResults.ofDocumentId); |
| 1425 | + } |
| 1426 | + } |
| 1427 | + }); |
| 1428 | + }); |
| 1429 | + |
| 1430 | + it('tears down Document Passage schema', () => { |
| 1431 | + return Promise.all([ |
| 1432 | + client.schema.classDeleter().withClassName('Passage').do(), |
| 1433 | + client.schema.classDeleter().withClassName('Document').do(), |
| 1434 | + ]); |
| 1435 | + }); |
| 1436 | +}); |
| 1437 | + |
1347 | 1438 | const setup = async (client: WeaviateClient) => { |
1348 | 1439 | const thing = { |
1349 | 1440 | class: 'Article', |
@@ -1474,3 +1565,127 @@ const setupReplicated = async (client: WeaviateClient) => { |
1474 | 1565 | await batch.do(); |
1475 | 1566 | return new Promise((resolve) => setTimeout(resolve, 1000)); |
1476 | 1567 | }; |
| 1568 | + |
| 1569 | +const setupGroupBy = async (client: WeaviateClient) => { |
| 1570 | + const document = { |
| 1571 | + class: 'Document', |
| 1572 | + invertedIndexConfig: { indexTimestamps: true }, |
| 1573 | + properties: [ |
| 1574 | + { |
| 1575 | + name: 'title', |
| 1576 | + dataType: ['text'], |
| 1577 | + }, |
| 1578 | + ], |
| 1579 | + }; |
| 1580 | + |
| 1581 | + const passage = { |
| 1582 | + class: 'Passage', |
| 1583 | + invertedIndexConfig: { indexTimestamps: true }, |
| 1584 | + properties: [ |
| 1585 | + { |
| 1586 | + name: 'content', |
| 1587 | + dataType: ['text'], |
| 1588 | + }, |
| 1589 | + { |
| 1590 | + name: 'type', |
| 1591 | + dataType: ['text'], |
| 1592 | + }, |
| 1593 | + { |
| 1594 | + name: 'ofDocument', |
| 1595 | + dataType: ['Document'], |
| 1596 | + }, |
| 1597 | + ], |
| 1598 | + }; |
| 1599 | + |
| 1600 | + await Promise.all([client.schema.classCreator().withClass(document).do()]); |
| 1601 | + await Promise.all([client.schema.classCreator().withClass(passage).do()]); |
| 1602 | + |
| 1603 | + // document, passage uuids |
| 1604 | + const documentIds: string[] = [ |
| 1605 | + '00000000-0000-0000-0000-00000000000a', |
| 1606 | + '00000000-0000-0000-0000-00000000000b', |
| 1607 | + '00000000-0000-0000-0000-00000000000c', |
| 1608 | + '00000000-0000-0000-0000-00000000000d', |
| 1609 | + ]; |
| 1610 | + |
| 1611 | + const passageIds: string[] = [ |
| 1612 | + '00000000-0000-0000-0000-000000000001', |
| 1613 | + '00000000-0000-0000-0000-000000000002', |
| 1614 | + '00000000-0000-0000-0000-000000000003', |
| 1615 | + '00000000-0000-0000-0000-000000000004', |
| 1616 | + '00000000-0000-0000-0000-000000000005', |
| 1617 | + '00000000-0000-0000-0000-000000000006', |
| 1618 | + '00000000-0000-0000-0000-000000000007', |
| 1619 | + '00000000-0000-0000-0000-000000000008', |
| 1620 | + '00000000-0000-0000-0000-000000000009', |
| 1621 | + '00000000-0000-0000-0000-000000000010', |
| 1622 | + '00000000-0000-0000-0000-000000000011', |
| 1623 | + '00000000-0000-0000-0000-000000000012', |
| 1624 | + '00000000-0000-0000-0000-000000000013', |
| 1625 | + '00000000-0000-0000-0000-000000000014', |
| 1626 | + '00000000-0000-0000-0000-000000000015', |
| 1627 | + '00000000-0000-0000-0000-000000000016', |
| 1628 | + '00000000-0000-0000-0000-000000000017', |
| 1629 | + '00000000-0000-0000-0000-000000000018', |
| 1630 | + '00000000-0000-0000-0000-000000000019', |
| 1631 | + '00000000-0000-0000-0000-000000000020', |
| 1632 | + ]; |
| 1633 | + |
| 1634 | + const documents: WeaviateObject[] = []; |
| 1635 | + for (let i = 0; i < documentIds.length; i++) { |
| 1636 | + documents.push({ |
| 1637 | + id: documentIds[i], |
| 1638 | + class: 'Document', |
| 1639 | + properties: { |
| 1640 | + title: `Title of the document ${i}`, |
| 1641 | + }, |
| 1642 | + }); |
| 1643 | + } |
| 1644 | + |
| 1645 | + const passages: WeaviateObject[] = []; |
| 1646 | + for (let i = 0; i < passageIds.length; i++) { |
| 1647 | + passages.push({ |
| 1648 | + id: passageIds[i], |
| 1649 | + class: 'Passage', |
| 1650 | + properties: { |
| 1651 | + content: `Passage content ${i}`, |
| 1652 | + type: 'document-passage', |
| 1653 | + }, |
| 1654 | + }); |
| 1655 | + } |
| 1656 | + |
| 1657 | + let batch = client.batch.objectsBatcher(); |
| 1658 | + [...documents, ...passages].forEach((elem) => { |
| 1659 | + batch = batch.withObject(elem); |
| 1660 | + }); |
| 1661 | + await batch.do(); |
| 1662 | + |
| 1663 | + const createReferences = ( |
| 1664 | + client: WeaviateClient, |
| 1665 | + document: WeaviateObject, |
| 1666 | + passages: WeaviateObject[] |
| 1667 | + ): void => { |
| 1668 | + const ref: Reference = client.data |
| 1669 | + .referencePayloadBuilder() |
| 1670 | + .withId(document.id!) |
| 1671 | + .withClassName(document.class!) |
| 1672 | + .payload(); |
| 1673 | + for (const passage of passages) { |
| 1674 | + client.data |
| 1675 | + .referenceCreator() |
| 1676 | + .withId(passage.id!) |
| 1677 | + .withClassName(passage.class!) |
| 1678 | + .withReferenceProperty('ofDocument') |
| 1679 | + .withReference(ref) |
| 1680 | + .do() |
| 1681 | + .catch((e: WeaviateError) => { |
| 1682 | + throw new Error('it should not have errord: ' + e); |
| 1683 | + }); |
| 1684 | + } |
| 1685 | + }; |
| 1686 | + |
| 1687 | + createReferences(client, documents[0], passages.slice(0, 10)); |
| 1688 | + createReferences(client, documents[1], passages.slice(10, 14)); |
| 1689 | + |
| 1690 | + return new Promise((resolve) => setTimeout(resolve, 1000)); |
| 1691 | +}; |
0 commit comments