Compare commits
7 Commits
master
...
Implement-
Author | SHA1 | Date | |
---|---|---|---|
|
dbda8484ee | ||
|
fd0f4a0346 | ||
|
dc2acf15e2 | ||
|
d4d6e85398 | ||
|
1558745e13 | ||
|
1788e60691 | ||
|
be32e8893d |
@ -119,6 +119,14 @@ modifications:
|
||||
value: "false"
|
||||
override: true
|
||||
|
||||
- name: "storage_enable_automatic_label_index_creation"
|
||||
value: "false"
|
||||
override: true
|
||||
|
||||
- name: "storage_enable_automatic_edge_type_index_creation"
|
||||
value: "false"
|
||||
override: true
|
||||
|
||||
- name: "query_callable_mappings_path"
|
||||
value: "/etc/memgraph/apoc_compatibility_mappings.json"
|
||||
override: true
|
||||
|
@ -131,6 +131,14 @@ DEFINE_uint64(storage_recovery_thread_count,
|
||||
DEFINE_bool(storage_enable_schema_metadata, false,
|
||||
"Controls whether metadata should be collected about the resident labels and edge types.");
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_bool(storage_enable_automatic_label_index_creation, false,
|
||||
"Controls whether label indexes on vertices should be created automatically.");
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_bool(storage_enable_automatic_edge_type_index_creation, false,
|
||||
"Controls whether edge-type indexes on relationships should be created automatically.");
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_bool(storage_delta_on_identical_property_update, true,
|
||||
"Controls whether updating a property with the same value should create a delta object.");
|
||||
|
@ -85,6 +85,10 @@ DECLARE_uint64(storage_recovery_thread_count);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_bool(storage_enable_schema_metadata);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_bool(storage_enable_automatic_label_index_creation);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_bool(storage_enable_automatic_edge_type_index_creation);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_bool(storage_delta_on_identical_property_update);
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
@ -333,8 +333,18 @@ int main(int argc, char **argv) {
|
||||
.wal_directory = FLAGS_data_directory + "/rocksdb_wal"},
|
||||
.salient.items = {.properties_on_edges = FLAGS_storage_properties_on_edges,
|
||||
.enable_schema_metadata = FLAGS_storage_enable_schema_metadata,
|
||||
.enable_label_index_auto_creation = FLAGS_storage_enable_automatic_label_index_creation,
|
||||
.enable_edge_type_index_auto_creation =
|
||||
FLAGS_storage_properties_on_edges ? FLAGS_storage_enable_automatic_edge_type_index_creation
|
||||
: false,
|
||||
.delta_on_identical_property_update = FLAGS_storage_delta_on_identical_property_update},
|
||||
.salient.storage_mode = memgraph::flags::ParseStorageMode()};
|
||||
if (db_config.salient.items.enable_edge_type_index_auto_creation && !db_config.salient.items.properties_on_edges) {
|
||||
spdlog::warn(
|
||||
"Automatic index creation on edge-types has been set but properties on edges are disabled. This will "
|
||||
"implicitly disallow automatic edge-type index creation. If you wish to use automatic edge-type index "
|
||||
"creation, enable properties on edges as well.");
|
||||
}
|
||||
spdlog::info("config recover on startup {}, flags {} {}", db_config.durability.recover_on_startup,
|
||||
FLAGS_storage_recover_on_startup, FLAGS_data_recovery_on_startup);
|
||||
memgraph::utils::Scheduler jemalloc_purge_scheduler;
|
||||
|
@ -37,6 +37,8 @@ struct SalientConfig {
|
||||
struct Items {
|
||||
bool properties_on_edges{true};
|
||||
bool enable_schema_metadata{false};
|
||||
bool enable_label_index_auto_creation{false};
|
||||
bool enable_edge_type_index_auto_creation{false};
|
||||
bool delta_on_identical_property_update{true};
|
||||
friend bool operator==(const Items &lrh, const Items &rhs) = default;
|
||||
} items;
|
||||
@ -45,13 +47,19 @@ struct SalientConfig {
|
||||
};
|
||||
|
||||
inline void to_json(nlohmann::json &data, SalientConfig::Items const &items) {
|
||||
data = nlohmann::json{{"properties_on_edges", items.properties_on_edges},
|
||||
{"enable_schema_metadata", items.enable_schema_metadata}};
|
||||
data = nlohmann::json{
|
||||
{"properties_on_edges", items.properties_on_edges},
|
||||
{"enable_schema_metadata", items.enable_schema_metadata},
|
||||
{"enable_label_index_auto_creation", items.enable_label_index_auto_creation},
|
||||
{"enable_edge_type_index_auto_creation", items.enable_edge_type_index_auto_creation},
|
||||
};
|
||||
}
|
||||
|
||||
inline void from_json(const nlohmann::json &data, SalientConfig::Items &items) {
|
||||
data.at("properties_on_edges").get_to(items.properties_on_edges);
|
||||
data.at("enable_schema_metadata").get_to(items.enable_schema_metadata);
|
||||
data.at("enable_label_index_auto_creation").get_to(items.enable_label_index_auto_creation);
|
||||
data.at("enable_edge_type_index_auto_creation").get_to(items.enable_edge_type_index_auto_creation);
|
||||
}
|
||||
|
||||
inline void to_json(nlohmann::json &data, SalientConfig const &config) {
|
||||
|
@ -1927,8 +1927,11 @@ void DiskStorage::DiskAccessor::FinalizeTransaction() {
|
||||
}
|
||||
}
|
||||
|
||||
utils::BasicResult<StorageIndexDefinitionError, void> DiskStorage::DiskAccessor::CreateIndex(LabelId label) {
|
||||
utils::BasicResult<StorageIndexDefinitionError, void> DiskStorage::DiskAccessor::CreateIndex(
|
||||
LabelId label, bool unique_access_needed) {
|
||||
if (unique_access_needed) {
|
||||
MG_ASSERT(unique_guard_.owns_lock(), "Create index requires unique access to the storage!");
|
||||
}
|
||||
auto *on_disk = static_cast<DiskStorage *>(storage_);
|
||||
auto *disk_label_index = static_cast<DiskLabelIndex *>(on_disk->indices_.label_index_.get());
|
||||
if (!disk_label_index->CreateIndex(label, on_disk->SerializeVerticesForLabelIndex(label))) {
|
||||
@ -1956,7 +1959,8 @@ utils::BasicResult<StorageIndexDefinitionError, void> DiskStorage::DiskAccessor:
|
||||
return {};
|
||||
}
|
||||
|
||||
utils::BasicResult<StorageIndexDefinitionError, void> DiskStorage::DiskAccessor::CreateIndex(EdgeTypeId /*edge_type*/) {
|
||||
utils::BasicResult<StorageIndexDefinitionError, void> DiskStorage::DiskAccessor::CreateIndex(
|
||||
EdgeTypeId /*edge_type*/, bool /*unique_access_needed*/) {
|
||||
throw utils::NotYetImplemented(
|
||||
"Edge-type index related operations are not yet supported using on-disk storage mode.");
|
||||
}
|
||||
|
@ -160,11 +160,13 @@ class DiskStorage final : public Storage {
|
||||
|
||||
void FinalizeTransaction() override;
|
||||
|
||||
utils::BasicResult<StorageIndexDefinitionError, void> CreateIndex(LabelId label) override;
|
||||
utils::BasicResult<StorageIndexDefinitionError, void> CreateIndex(LabelId label,
|
||||
bool unique_access_needed = true) override;
|
||||
|
||||
utils::BasicResult<StorageIndexDefinitionError, void> CreateIndex(LabelId label, PropertyId property) override;
|
||||
|
||||
utils::BasicResult<StorageIndexDefinitionError, void> CreateIndex(EdgeTypeId edge_type) override;
|
||||
utils::BasicResult<StorageIndexDefinitionError, void> CreateIndex(EdgeTypeId edge_type,
|
||||
bool unique_access_needed = true) override;
|
||||
|
||||
utils::BasicResult<StorageIndexDefinitionError, void> DropIndex(LabelId label) override;
|
||||
|
||||
|
@ -317,6 +317,11 @@ Result<EdgeAccessor> InMemoryStorage::InMemoryAccessor::CreateEdge(VertexAccesso
|
||||
guard_from.lock();
|
||||
}
|
||||
|
||||
if (storage_->config_.salient.items.enable_edge_type_index_auto_creation &&
|
||||
!storage_->indices_.edge_type_index_->IndexExists(edge_type)) {
|
||||
storage_->edge_types_to_auto_index_->emplace(edge_type);
|
||||
}
|
||||
|
||||
if (!PrepareForWrite(&transaction_, from_vertex)) return Error::SERIALIZATION_ERROR;
|
||||
if (from_vertex->deleted) return Error::DELETED_OBJECT;
|
||||
|
||||
@ -807,6 +812,25 @@ utils::BasicResult<StorageManipulationError, void> InMemoryStorage::InMemoryAcce
|
||||
|
||||
{
|
||||
std::unique_lock<utils::SpinLock> engine_guard(storage_->engine_lock_);
|
||||
|
||||
if (storage_->config_.salient.items.enable_label_index_auto_creation) {
|
||||
storage_->labels_to_auto_index_.WithLock([&](auto &label_indices) {
|
||||
for (const auto label : label_indices) {
|
||||
CreateIndex(label, false);
|
||||
}
|
||||
label_indices.clear();
|
||||
});
|
||||
}
|
||||
|
||||
if (storage_->config_.salient.items.enable_edge_type_index_auto_creation) {
|
||||
storage_->edge_types_to_auto_index_.WithLock([&](auto &edge_type_indices) {
|
||||
for (const auto edge_type : edge_type_indices) {
|
||||
CreateIndex(edge_type, false);
|
||||
}
|
||||
edge_type_indices.clear();
|
||||
});
|
||||
}
|
||||
|
||||
auto *mem_unique_constraints =
|
||||
static_cast<InMemoryUniqueConstraints *>(storage_->constraints_.unique_constraints_.get());
|
||||
commit_timestamp_.emplace(mem_storage->CommitTimestamp(reparg.desired_commit_timestamp));
|
||||
@ -1262,8 +1286,11 @@ void InMemoryStorage::InMemoryAccessor::FinalizeTransaction() {
|
||||
}
|
||||
}
|
||||
|
||||
utils::BasicResult<StorageIndexDefinitionError, void> InMemoryStorage::InMemoryAccessor::CreateIndex(LabelId label) {
|
||||
utils::BasicResult<StorageIndexDefinitionError, void> InMemoryStorage::InMemoryAccessor::CreateIndex(
|
||||
LabelId label, bool unique_access_needed) {
|
||||
if (unique_access_needed) {
|
||||
MG_ASSERT(unique_guard_.owns_lock(), "Creating label index requires a unique access to the storage!");
|
||||
}
|
||||
auto *in_memory = static_cast<InMemoryStorage *>(storage_);
|
||||
auto *mem_label_index = static_cast<InMemoryLabelIndex *>(in_memory->indices_.label_index_.get());
|
||||
if (!mem_label_index->CreateIndex(label, in_memory->vertices_.access(), std::nullopt)) {
|
||||
@ -1291,8 +1318,10 @@ utils::BasicResult<StorageIndexDefinitionError, void> InMemoryStorage::InMemoryA
|
||||
}
|
||||
|
||||
utils::BasicResult<StorageIndexDefinitionError, void> InMemoryStorage::InMemoryAccessor::CreateIndex(
|
||||
EdgeTypeId edge_type) {
|
||||
EdgeTypeId edge_type, bool unique_access_needed) {
|
||||
if (unique_access_needed) {
|
||||
MG_ASSERT(unique_guard_.owns_lock(), "Create index requires a unique access to the storage!");
|
||||
}
|
||||
auto *in_memory = static_cast<InMemoryStorage *>(storage_);
|
||||
auto *mem_edge_type_index = static_cast<InMemoryEdgeTypeIndex *>(in_memory->indices_.edge_type_index_.get());
|
||||
if (!mem_edge_type_index->CreateIndex(edge_type, in_memory->vertices_.access())) {
|
||||
|
@ -241,7 +241,8 @@ class InMemoryStorage final : public Storage {
|
||||
/// * `IndexDefinitionError`: the index already exists.
|
||||
/// * `ReplicationError`: there is at least one SYNC replica that has not confirmed receiving the transaction.
|
||||
/// @throw std::bad_alloc
|
||||
utils::BasicResult<StorageIndexDefinitionError, void> CreateIndex(LabelId label) override;
|
||||
utils::BasicResult<StorageIndexDefinitionError, void> CreateIndex(LabelId label,
|
||||
bool unique_access_needed = true) override;
|
||||
|
||||
/// Create an index.
|
||||
/// Returns void if the index has been created.
|
||||
@ -257,7 +258,8 @@ class InMemoryStorage final : public Storage {
|
||||
/// * `ReplicationError`: there is at least one SYNC replica that has not confirmed receiving the transaction.
|
||||
/// * `IndexDefinitionError`: the index already exists.
|
||||
/// @throw std::bad_alloc
|
||||
utils::BasicResult<StorageIndexDefinitionError, void> CreateIndex(EdgeTypeId edge_type) override;
|
||||
utils::BasicResult<StorageIndexDefinitionError, void> CreateIndex(EdgeTypeId edge_type,
|
||||
bool unique_access_needed = true) override;
|
||||
|
||||
/// Drop an existing index.
|
||||
/// Returns void if the index has been dropped.
|
||||
|
@ -299,11 +299,13 @@ class Storage {
|
||||
|
||||
std::vector<EdgeTypeId> ListAllPossiblyPresentEdgeTypes() const;
|
||||
|
||||
virtual utils::BasicResult<StorageIndexDefinitionError, void> CreateIndex(LabelId label) = 0;
|
||||
virtual utils::BasicResult<StorageIndexDefinitionError, void> CreateIndex(LabelId label,
|
||||
bool unique_access_needed = true) = 0;
|
||||
|
||||
virtual utils::BasicResult<StorageIndexDefinitionError, void> CreateIndex(LabelId label, PropertyId property) = 0;
|
||||
|
||||
virtual utils::BasicResult<StorageIndexDefinitionError, void> CreateIndex(EdgeTypeId edge_type) = 0;
|
||||
virtual utils::BasicResult<StorageIndexDefinitionError, void> CreateIndex(EdgeTypeId edge_type,
|
||||
bool unique_access_needed = true) = 0;
|
||||
|
||||
virtual utils::BasicResult<StorageIndexDefinitionError, void> DropIndex(LabelId label) = 0;
|
||||
|
||||
@ -466,6 +468,12 @@ class Storage {
|
||||
utils::SynchronizedMetaDataStore<LabelId> stored_node_labels_;
|
||||
utils::SynchronizedMetaDataStore<EdgeTypeId> stored_edge_types_;
|
||||
|
||||
// Sets that hold onto labels and edge-types that have to be created.
|
||||
// Used only if the label/edge-type autocreation flags are enabled,
|
||||
// does not affect index creation otherwise.
|
||||
utils::Synchronized<std::set<LabelId>, utils::SpinLock> labels_to_auto_index_;
|
||||
utils::Synchronized<std::set<EdgeTypeId>, utils::SpinLock> edge_types_to_auto_index_;
|
||||
|
||||
std::atomic<uint64_t> vertex_id_{0};
|
||||
std::atomic<uint64_t> edge_id_{0};
|
||||
};
|
||||
|
@ -118,6 +118,11 @@ Result<bool> VertexAccessor::AddLabel(LabelId label) {
|
||||
storage_->stored_node_labels_.try_insert(label);
|
||||
}
|
||||
|
||||
if (storage_->config_.salient.items.enable_label_index_auto_creation &&
|
||||
!storage_->indices_.label_index_->IndexExists(label)) {
|
||||
storage_->labels_to_auto_index_->emplace(label);
|
||||
}
|
||||
|
||||
/// TODO: some by pointers, some by reference => not good, make it better
|
||||
storage_->constraints_.unique_constraints_->UpdateOnAddLabel(label, *vertex_, transaction_->start_timestamp);
|
||||
if (transaction_->constraint_verification_info) transaction_->constraint_verification_info->AddedLabel(vertex_);
|
||||
|
@ -78,6 +78,7 @@ add_subdirectory(query_planning)
|
||||
add_subdirectory(awesome_functions)
|
||||
add_subdirectory(high_availability)
|
||||
add_subdirectory(concurrency)
|
||||
add_subdirectory(index_auto_creation)
|
||||
|
||||
add_subdirectory(replication_experimental)
|
||||
|
||||
|
@ -124,6 +124,16 @@ startup_config_dict = {
|
||||
"false",
|
||||
"Controls whether metadata should be collected about the resident labels and edge types.",
|
||||
),
|
||||
"storage_enable_automatic_label_index_creation": (
|
||||
"false",
|
||||
"false",
|
||||
"Controls whether label indexes on vertices should be created automatically.",
|
||||
),
|
||||
"storage_enable_automatic_edge_type_index_creation": (
|
||||
"false",
|
||||
"false",
|
||||
"Controls whether edge-type indexes on relationships should be created automatically.",
|
||||
),
|
||||
"password_encryption_algorithm": ("bcrypt", "bcrypt", "The password encryption algorithm used for authentication."),
|
||||
"pulsar_service_url": ("", "", "Default URL used while connecting to Pulsar brokers."),
|
||||
"query_execution_timeout_sec": (
|
||||
|
6
tests/e2e/index_auto_creation/CMakeLists.txt
Normal file
6
tests/e2e/index_auto_creation/CMakeLists.txt
Normal file
@ -0,0 +1,6 @@
|
||||
function(copy_auto_index_queries_e2e_python_files FILE_NAME)
|
||||
copy_e2e_python_files(index_auto_creation ${FILE_NAME})
|
||||
endfunction()
|
||||
|
||||
copy_auto_index_queries_e2e_python_files(common.py)
|
||||
copy_auto_index_queries_e2e_python_files(index_auto_creation.py)
|
27
tests/e2e/index_auto_creation/common.py
Normal file
27
tests/e2e/index_auto_creation/common.py
Normal file
@ -0,0 +1,27 @@
|
||||
# Copyright 2024 Memgraph Ltd.
|
||||
#
|
||||
# Use of this software is governed by the Business Source License
|
||||
# included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
# License, and you may not use this file except in compliance with the Business Source License.
|
||||
#
|
||||
# As of the Change Date specified in that file, in accordance with
|
||||
# the Business Source License, use of this software will be governed
|
||||
# by the Apache License, Version 2.0, included in the file
|
||||
# licenses/APL.txt.
|
||||
|
||||
import typing
|
||||
|
||||
import mgclient
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def cursor(**kwargs) -> mgclient.Connection:
|
||||
connection = mgclient.connect(host="localhost", port=7687, **kwargs)
|
||||
connection.autocommit = True
|
||||
return connection.cursor()
|
||||
|
||||
|
||||
def execute_and_fetch_all(cursor: mgclient.Cursor, query: str, params: dict = dict()) -> typing.List[tuple]:
|
||||
cursor.execute(query, params)
|
||||
return cursor.fetchall()
|
318
tests/e2e/index_auto_creation/index_auto_creation.py
Normal file
318
tests/e2e/index_auto_creation/index_auto_creation.py
Normal file
@ -0,0 +1,318 @@
|
||||
# Copyright 2024 Memgraph Ltd.
|
||||
#
|
||||
# Use of this software is governed by the Business Source License
|
||||
# included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
# License, and you may not use this file except in compliance with the Business Source License.
|
||||
#
|
||||
# As of the Change Date specified in that file, in accordance with
|
||||
# the Business Source License, use of this software will be governed
|
||||
# by the Apache License, Version 2.0, included in the file
|
||||
# licenses/APL.txt.
|
||||
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
from common import cursor, execute_and_fetch_all
|
||||
|
||||
|
||||
# Helper functions
|
||||
def get_index_stats(cursor):
|
||||
return execute_and_fetch_all(cursor, "SHOW INDEX INFO")
|
||||
|
||||
|
||||
def index_exists(indices, index_name):
|
||||
for index in indices:
|
||||
if index[1] == index_name:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def index_count_is(indices, index_name, count):
|
||||
return count == index_count(indices, index_name)
|
||||
|
||||
|
||||
def number_of_index_structures_are(indices, predicted_size):
|
||||
return len(indices) == predicted_size
|
||||
|
||||
|
||||
def index_count(indices, index_name):
|
||||
for index in indices:
|
||||
if index[1] == index_name:
|
||||
return index[3]
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
#####################
|
||||
# Label index tests #
|
||||
#####################
|
||||
|
||||
|
||||
def test_auto_create_single_label_index(cursor):
|
||||
label = "SOMELABEL"
|
||||
execute_and_fetch_all(cursor, f"CREATE (n:{label})")
|
||||
index_stats = get_index_stats(cursor)
|
||||
assert number_of_index_structures_are(index_stats, 1)
|
||||
assert index_exists(index_stats, label)
|
||||
assert index_count_is(index_stats, label, 1)
|
||||
|
||||
execute_and_fetch_all(cursor, f"DROP INDEX ON :{label}")
|
||||
index_stats = get_index_stats(cursor)
|
||||
assert number_of_index_structures_are(index_stats, 0)
|
||||
|
||||
execute_and_fetch_all(cursor, f"MATCH (n) DETACH DELETE n")
|
||||
|
||||
|
||||
def test_auto_create_multiple_label_index(cursor):
|
||||
label1 = "SOMELABEL1"
|
||||
execute_and_fetch_all(cursor, f"CREATE (n:{label1})")
|
||||
|
||||
label2 = "SOMELABEL2"
|
||||
execute_and_fetch_all(cursor, f"CREATE (n:{label2})")
|
||||
|
||||
label3 = "SOMELABEL3"
|
||||
execute_and_fetch_all(cursor, f"CREATE (n:{label3})")
|
||||
|
||||
index_stats = get_index_stats(cursor)
|
||||
|
||||
assert number_of_index_structures_are(index_stats, 3)
|
||||
assert index_exists(index_stats, label1)
|
||||
assert index_exists(index_stats, label2)
|
||||
assert index_exists(index_stats, label3)
|
||||
assert index_count_is(index_stats, label1, 1)
|
||||
assert index_count_is(index_stats, label2, 1)
|
||||
assert index_count_is(index_stats, label3, 1)
|
||||
|
||||
execute_and_fetch_all(cursor, f"DROP INDEX ON :{label1}")
|
||||
index_stats = get_index_stats(cursor)
|
||||
assert number_of_index_structures_are(index_stats, 2)
|
||||
|
||||
execute_and_fetch_all(cursor, f"DROP INDEX ON :{label2}")
|
||||
index_stats = get_index_stats(cursor)
|
||||
assert number_of_index_structures_are(index_stats, 1)
|
||||
|
||||
execute_and_fetch_all(cursor, f"DROP INDEX ON :{label3}")
|
||||
index_stats = get_index_stats(cursor)
|
||||
assert number_of_index_structures_are(index_stats, 0)
|
||||
|
||||
execute_and_fetch_all(cursor, f"MATCH (n) DETACH DELETE n")
|
||||
|
||||
|
||||
def test_auto_create_single_label_index_with_multiple_entries(cursor):
|
||||
label = "SOMELABEL"
|
||||
for _ in range(100):
|
||||
execute_and_fetch_all(cursor, f"CREATE (n:{label})")
|
||||
index_stats = get_index_stats(cursor)
|
||||
|
||||
print(len(index_stats[0]))
|
||||
|
||||
assert number_of_index_structures_are(index_stats, 1)
|
||||
assert index_exists(index_stats, label)
|
||||
assert index_count_is(index_stats, label, 100)
|
||||
|
||||
execute_and_fetch_all(cursor, f"DROP INDEX ON :{label}")
|
||||
index_stats = get_index_stats(cursor)
|
||||
assert number_of_index_structures_are(index_stats, 0)
|
||||
|
||||
execute_and_fetch_all(cursor, f"MATCH (n) DETACH DELETE n")
|
||||
|
||||
|
||||
def test_auto_create_multiple_label_index_with_multiple_entries(cursor):
|
||||
label1 = "SOMELABEL1"
|
||||
for _ in range(120):
|
||||
execute_and_fetch_all(cursor, f"CREATE (n:{label1})")
|
||||
|
||||
label2 = "SOMELABEL2"
|
||||
for _ in range(100):
|
||||
execute_and_fetch_all(cursor, f"CREATE (n:{label2})")
|
||||
|
||||
label3 = "SOMELABEL3"
|
||||
for _ in range(80):
|
||||
execute_and_fetch_all(cursor, f"CREATE (n:{label3})")
|
||||
|
||||
index_stats = get_index_stats(cursor)
|
||||
|
||||
print(index_stats)
|
||||
|
||||
assert number_of_index_structures_are(index_stats, 3)
|
||||
assert index_exists(index_stats, label1)
|
||||
assert index_exists(index_stats, label2)
|
||||
assert index_exists(index_stats, label3)
|
||||
assert index_count_is(index_stats, label1, 120)
|
||||
assert index_count_is(index_stats, label2, 100)
|
||||
assert index_count_is(index_stats, label3, 80)
|
||||
|
||||
execute_and_fetch_all(cursor, f"DROP INDEX ON :{label1}")
|
||||
index_stats = get_index_stats(cursor)
|
||||
assert number_of_index_structures_are(index_stats, 2)
|
||||
|
||||
execute_and_fetch_all(cursor, f"DROP INDEX ON :{label2}")
|
||||
index_stats = get_index_stats(cursor)
|
||||
assert number_of_index_structures_are(index_stats, 1)
|
||||
|
||||
execute_and_fetch_all(cursor, f"DROP INDEX ON :{label3}")
|
||||
index_stats = get_index_stats(cursor)
|
||||
assert number_of_index_structures_are(index_stats, 0)
|
||||
|
||||
execute_and_fetch_all(cursor, f"MATCH (n) DETACH DELETE n")
|
||||
|
||||
|
||||
#########################
|
||||
# Edge-type index tests #
|
||||
#########################
|
||||
|
||||
|
||||
def test_auto_create_single_edge_type_index(cursor):
|
||||
label_from = "LABEL_FROM"
|
||||
label_to = "LABEL_TO"
|
||||
edge_type = "SOMEEDGETYPE"
|
||||
execute_and_fetch_all(cursor, f"CREATE (n:{label_from})")
|
||||
execute_and_fetch_all(cursor, f"CREATE (n:{label_to})")
|
||||
execute_and_fetch_all(cursor, f"MATCH (n:{label_from}), (m:{label_to}) CREATE (n)-[:{edge_type}]->(m)")
|
||||
|
||||
index_stats = get_index_stats(cursor)
|
||||
assert index_exists(index_stats, label_from)
|
||||
assert index_exists(index_stats, label_to)
|
||||
assert index_exists(index_stats, edge_type)
|
||||
assert index_count_is(index_stats, label_from, 1)
|
||||
assert index_count_is(index_stats, label_to, 1)
|
||||
assert index_count_is(index_stats, edge_type, 1)
|
||||
assert number_of_index_structures_are(index_stats, 3) # 2 label + 1 edge-type
|
||||
|
||||
execute_and_fetch_all(cursor, f"DROP INDEX ON :{label_from}")
|
||||
execute_and_fetch_all(cursor, f"DROP INDEX ON :{label_to}")
|
||||
execute_and_fetch_all(cursor, f"DROP EDGE INDEX ON :{edge_type}")
|
||||
index_stats = get_index_stats(cursor)
|
||||
assert number_of_index_structures_are(index_stats, 0)
|
||||
|
||||
execute_and_fetch_all(cursor, f"MATCH (n) DETACH DELETE n")
|
||||
|
||||
|
||||
def test_auto_create_multiple_edge_type_index(cursor):
|
||||
label_from = "LABEL_FROM"
|
||||
label_to = "LABEL_TO"
|
||||
edge_type1 = "SOMEEDGETYPE1"
|
||||
edge_type2 = "SOMEEDGETYPE2"
|
||||
edge_type3 = "SOMEEDGETYPE3"
|
||||
|
||||
execute_and_fetch_all(cursor, f"CREATE (n:{label_from})")
|
||||
execute_and_fetch_all(cursor, f"CREATE (n:{label_to})")
|
||||
execute_and_fetch_all(cursor, f"MATCH (n:{label_from}), (m:{label_to}) CREATE (n)-[:{edge_type1}]->(m)")
|
||||
execute_and_fetch_all(cursor, f"MATCH (n:{label_from}), (m:{label_to}) CREATE (n)-[:{edge_type2}]->(m)")
|
||||
execute_and_fetch_all(cursor, f"MATCH (n:{label_from}), (m:{label_to}) CREATE (n)-[:{edge_type3}]->(m)")
|
||||
|
||||
index_stats = get_index_stats(cursor)
|
||||
|
||||
assert number_of_index_structures_are(index_stats, 5) # 2 label + 3 edge-type
|
||||
assert index_exists(index_stats, label_from)
|
||||
assert index_exists(index_stats, label_to)
|
||||
assert index_exists(index_stats, edge_type1)
|
||||
assert index_exists(index_stats, edge_type2)
|
||||
assert index_exists(index_stats, edge_type3)
|
||||
assert index_count_is(index_stats, edge_type1, 1)
|
||||
assert index_count_is(index_stats, edge_type2, 1)
|
||||
assert index_count_is(index_stats, edge_type3, 1)
|
||||
|
||||
execute_and_fetch_all(cursor, f"DROP INDEX ON :{label_from}")
|
||||
index_stats = get_index_stats(cursor)
|
||||
assert number_of_index_structures_are(index_stats, 4)
|
||||
|
||||
execute_and_fetch_all(cursor, f"DROP INDEX ON :{label_to}")
|
||||
index_stats = get_index_stats(cursor)
|
||||
assert number_of_index_structures_are(index_stats, 3)
|
||||
|
||||
execute_and_fetch_all(cursor, f"DROP EDGE INDEX ON :{edge_type1}")
|
||||
index_stats = get_index_stats(cursor)
|
||||
assert number_of_index_structures_are(index_stats, 2)
|
||||
|
||||
execute_and_fetch_all(cursor, f"DROP EDGE INDEX ON :{edge_type2}")
|
||||
index_stats = get_index_stats(cursor)
|
||||
assert number_of_index_structures_are(index_stats, 1)
|
||||
|
||||
execute_and_fetch_all(cursor, f"DROP EDGE INDEX ON :{edge_type3}")
|
||||
index_stats = get_index_stats(cursor)
|
||||
assert number_of_index_structures_are(index_stats, 0)
|
||||
|
||||
execute_and_fetch_all(cursor, f"MATCH (n) DETACH DELETE n")
|
||||
|
||||
|
||||
def test_auto_create_single_edge_type_index_with_multiple_entries(cursor):
|
||||
label_from = "LABEL_FROM"
|
||||
label_to = "LABEL_TO"
|
||||
edge_type = "SOMEEDGETYPE"
|
||||
execute_and_fetch_all(cursor, f"CREATE (n:{label_from})")
|
||||
execute_and_fetch_all(cursor, f"CREATE (n:{label_to})")
|
||||
for _ in range(100):
|
||||
execute_and_fetch_all(cursor, f"MATCH (n:{label_from}), (m:{label_to}) CREATE (n)-[:{edge_type}]->(m)")
|
||||
|
||||
index_stats = get_index_stats(cursor)
|
||||
assert index_exists(index_stats, label_from)
|
||||
assert index_exists(index_stats, label_to)
|
||||
assert index_exists(index_stats, edge_type)
|
||||
assert number_of_index_structures_are(index_stats, 3) # 2 label + 1 edge-type
|
||||
|
||||
assert index_count_is(index_stats, edge_type, 100)
|
||||
|
||||
execute_and_fetch_all(cursor, f"DROP INDEX ON :{label_from}")
|
||||
execute_and_fetch_all(cursor, f"DROP INDEX ON :{label_to}")
|
||||
execute_and_fetch_all(cursor, f"DROP EDGE INDEX ON :{edge_type}")
|
||||
index_stats = get_index_stats(cursor)
|
||||
assert number_of_index_structures_are(index_stats, 0)
|
||||
|
||||
execute_and_fetch_all(cursor, f"MATCH (n) DETACH DELETE n")
|
||||
|
||||
|
||||
def test_auto_create_multiple_edge_type_index_with_multiple_entries(cursor):
|
||||
label_from = "LABEL_FROM"
|
||||
label_to = "LABEL_TO"
|
||||
edge_type1 = "SOMEEDGETYPE1"
|
||||
edge_type2 = "SOMEEDGETYPE2"
|
||||
edge_type3 = "SOMEEDGETYPE3"
|
||||
|
||||
execute_and_fetch_all(cursor, f"CREATE (n:{label_from})")
|
||||
execute_and_fetch_all(cursor, f"CREATE (n:{label_to})")
|
||||
for _ in range(120):
|
||||
execute_and_fetch_all(cursor, f"MATCH (n:{label_from}), (m:{label_to}) CREATE (n)-[:{edge_type1}]->(m)")
|
||||
for _ in range(100):
|
||||
execute_and_fetch_all(cursor, f"MATCH (n:{label_from}), (m:{label_to}) CREATE (n)-[:{edge_type2}]->(m)")
|
||||
for _ in range(80):
|
||||
execute_and_fetch_all(cursor, f"MATCH (n:{label_from}), (m:{label_to}) CREATE (n)-[:{edge_type3}]->(m)")
|
||||
|
||||
index_stats = get_index_stats(cursor)
|
||||
|
||||
assert number_of_index_structures_are(index_stats, 5) # 2 label + 3 edge-type
|
||||
assert index_exists(index_stats, label_from)
|
||||
assert index_exists(index_stats, label_to)
|
||||
assert index_exists(index_stats, edge_type1)
|
||||
assert index_exists(index_stats, edge_type2)
|
||||
assert index_exists(index_stats, edge_type3)
|
||||
assert index_count_is(index_stats, edge_type1, 120)
|
||||
assert index_count_is(index_stats, edge_type2, 100)
|
||||
assert index_count_is(index_stats, edge_type3, 80)
|
||||
|
||||
execute_and_fetch_all(cursor, f"DROP INDEX ON :{label_from}")
|
||||
index_stats = get_index_stats(cursor)
|
||||
assert number_of_index_structures_are(index_stats, 4)
|
||||
|
||||
execute_and_fetch_all(cursor, f"DROP INDEX ON :{label_to}")
|
||||
index_stats = get_index_stats(cursor)
|
||||
assert number_of_index_structures_are(index_stats, 3)
|
||||
|
||||
execute_and_fetch_all(cursor, f"DROP EDGE INDEX ON :{edge_type1}")
|
||||
index_stats = get_index_stats(cursor)
|
||||
assert number_of_index_structures_are(index_stats, 2)
|
||||
|
||||
execute_and_fetch_all(cursor, f"DROP EDGE INDEX ON :{edge_type2}")
|
||||
index_stats = get_index_stats(cursor)
|
||||
assert number_of_index_structures_are(index_stats, 1)
|
||||
|
||||
execute_and_fetch_all(cursor, f"DROP EDGE INDEX ON :{edge_type3}")
|
||||
index_stats = get_index_stats(cursor)
|
||||
assert number_of_index_structures_are(index_stats, 0)
|
||||
|
||||
execute_and_fetch_all(cursor, f"MATCH (n) DETACH DELETE n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(pytest.main([__file__, "-rA"]))
|
13
tests/e2e/index_auto_creation/workloads.yaml
Normal file
13
tests/e2e/index_auto_creation/workloads.yaml
Normal file
@ -0,0 +1,13 @@
|
||||
index_auto_creation: &index_auto_creation
|
||||
cluster:
|
||||
main:
|
||||
args: ["--bolt-port", "7687", "--log-level=TRACE", "--also-log-to-stderr", "--storage-properties-on-edges=TRUE", "--storage-enable-automatic-label-index-creation=TRUE", "--storage-enable-automatic-edge-type-index-creation=TRUE"]
|
||||
log_file: "index_auto_creation.log"
|
||||
setup_queries: []
|
||||
validation_queries: []
|
||||
|
||||
workloads:
|
||||
- name: "Label index auto creation"
|
||||
binary: "tests/e2e/pytest_runner.sh"
|
||||
args: ["index_auto_creation/index_auto_creation.py"]
|
||||
<<: *index_auto_creation
|
Loading…
Reference in New Issue
Block a user