Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 21 additions & 21 deletions docs/ShortestPath/Guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ We can provide the source and target sets through patterns that come before the
```
turing:dijkstraMinimalExample> match (n{name:"Ashchurch"}), (m{name:"Worcestershire Parkway"}) shortestPath(n,m,distance,dist,path) return dist,path

+-------+--------------+
| dist | path |
+-------+--------------+
| 11.29 | (0)-[1]->(6) |
+-------+--------------+
+-------+---------------+
| dist | path |
+-------+---------------+
| 11.29 | (0), [1], (6) |
+-------+---------------+
```

note that we cannot access the variables of any branch being used by the shortest path processor:
Expand All @@ -34,11 +34,11 @@ we can access branches that aren't being used by the shortest path processor (th
```
turing:dijkstraMinimalExample> match (n{name:"Ashchurch"}), (m{name:"Worcestershire Parkway"}), (p{name:"Cheltenham Spa"}) shortestPath(n,m,distance,dist,path) return dist,path,p.name

+-------+--------------+----------------+
| dist | path | p.name |
+-------+--------------+----------------+
| 11.29 | (0)-[1]->(6) | Cheltenham Spa |
+-------+--------------+----------------+
+-------+---------------+----------------+
| dist | path | p.name |
+-------+---------------+----------------+
| 11.29 | (0), [1], (6) | Cheltenham Spa |
+-------+---------------+----------------+

```

Expand All @@ -55,14 +55,14 @@ turing:dijkstraMinimalExample> match (n{name:"Ashchurch"}), (m{name:"Worcester
+-------+
```

The path variable is a string representing the shortest path (in the format ...(nodeID)-\[edgeID\]->(nodeID)...)
The path variable is the shortest path from source to target: its nodes and edges in order, a node as (nodeID) and an edge as \[edgeID\]
```
turing:dijkstraMinimalExample> match (n{name:"Ashchurch"}), (m{name:"Worcestershire Parkway"}) shortestPath(n,m,distance,dist,path) return path
+--------------+
| path |
+--------------+
| (0)-[1]->(6) |
+--------------+
+---------------+
| path |
+---------------+
| (0), [1], (6) |
+---------------+
```

As you can see the path does match the ids
Expand Down Expand Up @@ -104,11 +104,11 @@ As you can see below we can pass multiple sources to the shortest path processor
```
turing:dijkstraMinimalExample> match (n), (m{name:"Worcestershire Parkway"}) where n.name="Bromsgrove" or n.name = "Cheltenham Spa" shortestPath(n,m,distance,dist,path) return dist,path

+------+--------------+
| dist | path |
+------+--------------+
| 12.6 | (1)-[5]->(6) |
+------+--------------+
+------+---------------+
| dist | path |
+------+---------------+
| 12.6 | (1), [5], (6) |
+------+---------------+
```
### Cypher Sample
the following cypher sample was used in the above examples
Expand Down
30 changes: 15 additions & 15 deletions docs/ShortestPath/shortest_path.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,11 @@ shortestPath(sourceSet, targetSet, edgePropName, distanceReturnVar, pathReturnVa
</Tabs>

```
+-------+--------------+
| dist | path |
+-------+--------------+
| 11.29 | (0)-[1]->(6) |
+-------+--------------+
+-------+---------------+
| dist | path |
+-------+---------------+
| 11.29 | (0), [1], (6) |
+-------+---------------+
```

### Returning only the distance or only the path
Expand Down Expand Up @@ -137,11 +137,11 @@ You can pass multiple nodes to the source or target set — `shortestPath` will
</Tabs>

```
+------+--------------+
| dist | path |
+------+--------------+
| 12.6 | (1)-[5]->(6) |
+------+--------------+
+------+---------------+
| dist | path |
+------+---------------+
| 12.6 | (1), [5], (6) |
+------+---------------+
```

## Limitations
Expand Down Expand Up @@ -170,10 +170,10 @@ You can pass multiple nodes to the source or target set — `shortestPath` will
```

```
+-------+--------------+----------------+
| dist | path | p.name |
+-------+--------------+----------------+
| 11.29 | (0)-[1]->(6) | Cheltenham Spa |
+-------+--------------+----------------+
+-------+---------------+----------------+
| dist | path | p.name |
+-------+---------------+----------------+
| 11.29 | (0), [1], (6) | Cheltenham Spa |
+-------+---------------+----------------+
```
</Tip>
3 changes: 1 addition & 2 deletions memory/LocalMemory.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,7 @@ class LocalMemory {
MakeMemoryPool<ColumnVector<NodeID>>::type,
MakeMemoryPool<ColumnVector<EdgeID>>::type,
MakeMemoryPool<ColumnVector<PathRef>>::type,
MakeMemoryPool<ColumnVector<Path>>::type,
MakeMemoryPool<ColumnVector<EntityList>>::type,
MakeMemoryPool<ColumnVector<GraphPath>>::type,
MakeMemoryPool<ColumnVector<LabelID>>::type,
MakeMemoryPool<ColumnVector<LabelSetID>>::type,
MakeMemoryPool<ColumnVector<EdgeTypeID>>::type,
Expand Down
9 changes: 4 additions & 5 deletions net/decoders/DecodeContext.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#pragma once

#include <stddef.h>
#include <optional>

namespace net::proto {

Expand Down Expand Up @@ -32,9 +31,9 @@ struct DecodeContext {
// Pointer to the TuringProto receive buffer
TuringProtoInBuf* _inBuf {nullptr};
InterruptedBufferState _bufferState;
// Entry count of the EntityList row being decoded. The count and actual data can be
// split across chunks so we need to store state here.
std::optional<size_t> _entityListEntryCount;
// Set once the nodes of the path row being decoded have arrived, so a resumed pass
// goes on to its edges rather than reading a node count again.
bool _pathNodesRead {false};
// Set once a constant list column's header has been read and its space reserved, so a
// resumed pass drains elements rather than reading the header again. An optional
// constant spends _rowIndex on its has-value flag, so the two cannot share it.
Expand All @@ -44,7 +43,7 @@ struct DecodeContext {
_columnIndex = 0;
_rowIndex = 0;
_bufferState.reset();
_entityListEntryCount.reset();
_pathNodesRead = false;
_constListStarted = false;
}
};
Expand Down
120 changes: 27 additions & 93 deletions net/decoders/Decoders.h
Original file line number Diff line number Diff line change
Expand Up @@ -236,34 +236,50 @@ struct VectorColumnDecoder<SinkMapEntryView<Sink>, Sink> {
}
};

// A path is [nodeCount][nodes][edges], with one edge fewer than nodes, or none for the
// null path.
template <ProtoDecodeSink Sink>
struct VectorColumnDecoder<db::Path, Sink> {
using T = db::Path;
struct VectorColumnDecoder<db::GraphPath, Sink> {
using T = db::GraphPath;

static bool decode(DecodeContext* context,
Sink* sink,
SinkColumnVector<T, Sink>* typedColumn,
ProtoColumnState* columnState) {
if (context->_rowIndex == 0) {
typedColumn->reserve(columnState->getNumRows());
typedColumn->resize(columnState->getNumRows());
}

for (size_t i = 0; context->_rowIndex + i < columnState->getNumRows(); ++i) {
if (context->_inBuf->readable() < sizeof(WireSize)) {
context->_rowIndex += i;
return false;
db::GraphPath& path = (*typedColumn)[context->_rowIndex + i];

if (!context->_pathNodesRead) {
if (context->_inBuf->readable() < sizeof(WireSize)) {
context->_rowIndex += i;
return false;
}

WireSize nodeCount = 0;
context->_inBuf->readData(&nodeCount, sizeof(nodeCount));
path.resize(nodeCount, nodeCount == 0 ? 0 : nodeCount - 1);

const std::span<db::NodeID> nodes = path.getNodes();
if (!readVarLenPayload(context, reinterpret_cast<char*>(nodes.data()), nodes.size_bytes())) {
context->_rowIndex += i;
context->_pathNodesRead = true;
return false;
}
}

WireSize pathByteSize = 0;
context->_inBuf->readData(&pathByteSize, sizeof(pathByteSize));
const size_t numEntities = checkedElementCount<db::EntityID>(pathByteSize, "path");
context->_pathNodesRead = false;

auto& path = typedColumn->emplace_back(numEntities);
if (!readVarLenPayload(context, reinterpret_cast<char*>(path.data()), pathByteSize)) {
const std::span<db::EdgeID> edges = path.getEdges();
if (!readVarLenPayload(context, reinterpret_cast<char*>(edges.data()), edges.size_bytes())) {
context->_rowIndex += i + 1;
return false;
}
}

return true;
}
};
Expand Down Expand Up @@ -303,59 +319,6 @@ struct VectorColumnDecoder<db::types::Embedding::Primitive, Sink> {
}
};

template <ProtoDecodeSink Sink>
struct VectorColumnDecoder<db::EntityList, Sink> {
using T = db::EntityList;

static bool decode(DecodeContext* context,
Sink* sink,
SinkColumnVector<T, Sink>* typedColumn,
ProtoColumnState* columnState) {
if (context->_rowIndex == 0) {
typedColumn->resize(columnState->getNumRows());
}

for (size_t i = 0; context->_rowIndex + i < columnState->getNumRows(); ++i) {
auto& entityList = (*typedColumn)[context->_rowIndex + i];

if (!context->_entityListEntryCount.has_value()) {
if (context->_inBuf->readable() < sizeof(WireSize)) {
context->_rowIndex += i;
return false;
}

WireSize numberOfEntries = 0;
context->_inBuf->readData(&numberOfEntries, sizeof(numberOfEntries));
context->_entityListEntryCount = numberOfEntries;
entityList.reserve(numberOfEntries);
}

const size_t expectedEntries = *context->_entityListEntryCount;

while (entityList.size() < expectedEntries) {
if (context->_inBuf->readable() < sizeof(db::EntityList::Entry::_type)) {
context->_rowIndex += i;
return false;
}

entityList.add();

context->_inBuf->readData(&entityList.back()._type, sizeof(db::EntityList::Entry::_type));

if (context->_inBuf->readable() < sizeof(db::EntityList::Entry::_id)) {
throw TuringException("Entity List Entry can't be broken up across packet/buffer boundaries");
}

context->_inBuf->readData(&entityList.back()._id, sizeof(db::EntityList::Entry::_id));
}

context->_entityListEntryCount.reset();
}

return true;
}
};

template <typename T, ProtoDecodeSink Sink>
struct OptionalVectorColumnDecoder {
static bool decode(DecodeContext* context,
Expand Down Expand Up @@ -635,35 +598,6 @@ struct ConstColumnDecoder<db::types::String::Primitive, Sink> {
}
};

template <ProtoDecodeSink Sink>
struct ConstColumnDecoder<db::Path, Sink> {
using T = db::Path;

template <ConstColumnOf<T, Sink> Column>
static bool decode(DecodeContext* context, Sink* sink, Column* typedColumn) {
if (context->_inBuf->readable() < sizeof(WireSize)) {
return false;
}

WireSize pathByteSize = 0;
context->_inBuf->readData(&pathByteSize, sizeof(pathByteSize));
const size_t numEntities = checkedElementCount<db::EntityID>(pathByteSize, "path");

db::Path path(numEntities);
char* pathBytes = reinterpret_cast<char*>(path.data());

// db::Path is a std::vector, so moving it into the column keeps pathBytes valid.
typedColumn->set(std::move(path));
if (!readVarLenPayload(context, pathBytes, pathByteSize)) {
// See the string branch: completing the resume completes the column.
++context->_columnIndex;
context->_rowIndex = 0;
return false;
}
return true;
}
};

template <ProtoDecodeSink Sink>
struct ConstColumnDecoder<db::types::Embedding::Primitive, Sink> {
using T = db::types::Embedding::Primitive;
Expand Down
6 changes: 2 additions & 4 deletions net/decoders/TuringProtoDecoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,14 +88,10 @@ decltype(auto) TuringProtoDecoder<Sink>::dispatchColumnType(ColumnInternalKind t
return fn.template operator()<db::types::String::Primitive>(encoding);
case ColumnInternalKind::BOOL:
return fn.template operator()<db::types::Bool::Primitive>(encoding);
case ColumnInternalKind::PATH:
return fn.template operator()<db::Path>(encoding);
case ColumnInternalKind::EMBEDDING:
return fn.template operator()<db::types::Embedding::Primitive>(encoding);
case ColumnInternalKind::VALUE_TYPE:
return fn.template operator()<db::ValueType>(encoding);
case ColumnInternalKind::ENTITY_LIST:
return fn.template operator()<db::EntityList>(encoding);
case ColumnInternalKind::LIST_VIEW:
return fn.template operator()<SinkListView<Sink>>(encoding);
case net::proto::ColumnInternalKind::MAP_VIEW:
Expand Down Expand Up @@ -126,6 +122,8 @@ decltype(auto) TuringProtoDecoder<Sink>::dispatchColumnType(ColumnInternalKind t
return fn.template operator()<db::types::DateTime::Primitive>(encoding);
case ColumnInternalKind::DURATION:
return fn.template operator()<db::types::Duration::Primitive>(encoding);
case ColumnInternalKind::GRAPH_PATH:
return fn.template operator()<db::GraphPath>(encoding);
}

throw TuringException("Unsupported incoming column type");
Expand Down
4 changes: 1 addition & 3 deletions net/decoders/TuringProtoDecoderConcepts.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

#include <type_traits>

#include "EntityList.h"
#include "GraphPath.h"
#include "metadata/PropertyNull.h"
#include "metadata/PropertyType.h"
Expand Down Expand Up @@ -42,8 +41,7 @@ concept SupportedColumnVectorTypes = std::is_same_v<T, db::types::UInt64::Primit
|| std::is_same_v<T, db::LabelID>
|| std::is_same_v<T, db::LabelSetID>
|| std::is_same_v<T, db::ChangeID>
|| std::is_same_v<T, db::Path>
|| std::is_same_v<T, db::EntityList>
|| std::is_same_v<T, db::GraphPath>
|| std::is_same_v<T, typename Sink::ListElementView>
|| std::is_same_v<T, typename Sink::ListView>
|| std::is_same_v<T, typename Sink::MapView>
Expand Down
Loading
Loading