diff --git a/mod/box/box.m b/mod/box/box.m
index 8361a76680bf15414c4b99976d797a6979a2b668..4d4b08a9dc4ee15b5ba081b3f893585178150228 100644
--- a/mod/box/box.m
+++ b/mod/box/box.m
@@ -1017,7 +1017,7 @@ prepare_update(struct box_txn *txn, struct tbuf *data)
 	struct update_cmd *cmd = parse_update_cmd(data);
 
 	/* Try to find the tuple. */
-	txn->old_tuple = [txn->index find :cmd->key :cmd->key_cardinality];
+	txn->old_tuple = [txn->index findByKey :cmd->key :cmd->key_cardinality];
 	if (txn->old_tuple == NULL) {
 		/* Not found. For simplicity, skip the logging. */
 		txn->flags |= BOX_NOT_STORE;
@@ -1070,7 +1070,7 @@ process_select(struct box_txn *txn, u32 limit, u32 offset, struct tbuf *data)
 		read_key(data, &key, &key_cardinality);
 
 		struct iterator *it = index->position;
-		[index initIterator: it :ITER_FORWARD :key :key_cardinality];
+		[index initIteratorByKey: it :ITER_FORWARD :key :key_cardinality];
 
 		while ((tuple = it->next_equal(it)) != NULL) {
 			if (tuple->flags & GHOST)
@@ -1101,7 +1101,7 @@ prepare_delete(struct box_txn *txn, struct tbuf *data)
 	void *key;
 	read_key(data, &key, &key_cardinality);
 	/* try to find tuple in primary index */
-	txn->old_tuple = [txn->index find :key :key_cardinality];
+	txn->old_tuple = [txn->index findByKey :key :key_cardinality];
 
 	if (txn->old_tuple == NULL) {
 		/*
diff --git a/mod/box/box_lua.m b/mod/box/box_lua.m
index 8ee57c9bbbe1af02d66545970e09c692cf851787..332c512b9041b312a28dbf2bd39f24d79394ec30 100644
--- a/mod/box/box_lua.m
+++ b/mod/box/box_lua.m
@@ -436,7 +436,7 @@ lbox_index_move(struct lua_State *L, enum iterator_type type)
 				   "does not match index cardinality (%d)",
 				   cardinality, index->key_def->part_count);
 		it = [index allocIterator];
-		[index initIterator: it :type :key :cardinality];
+		[index initIteratorByKey: it :type :key :cardinality];
 		lbox_pushiterator(L, it);
 	} else { /* 1 item on the stack and it's a userdata. */
 		it = lua_checkiterator(L, 2);
diff --git a/mod/box/index.h b/mod/box/index.h
index 99a6d72dc80987f7f8c1eb8abad5d0f9b7502bc8..d38ad1a2f1846f0d72d88bece1d4a2aad6a77895 100644
--- a/mod/box/index.h
+++ b/mod/box/index.h
@@ -117,7 +117,7 @@ struct key_def {
 - (size_t) size;
 - (struct box_tuple *) min;
 - (struct box_tuple *) max;
-- (struct box_tuple *) find: (void *) key :(int) key_cardinality;
+- (struct box_tuple *) findByKey: (void *) key :(int) parts;
 - (struct box_tuple *) findByTuple: (struct box_tuple *) tuple;
 - (void) remove: (struct box_tuple *) tuple;
 - (void) replace: (struct box_tuple *) old_tuple :(struct box_tuple *) new_tuple;
@@ -126,10 +126,22 @@ struct key_def {
  * initialized separately.
  */
 - (struct iterator *) allocIterator;
-- (void) initIterator: (struct iterator *) iterator :(enum iterator_type) type;
-- (void) initIterator: (struct iterator *) iterator :(enum iterator_type) type
-			:(void *) key
-			:(int) part_count;
+- (void) initIterator: (struct iterator *) iterator
+			:(enum iterator_type) type;
+- (void) initIteratorByKey: (struct iterator *) iterator
+			:(enum iterator_type) type
+			:(void *) key :(int) parts;
+/**
+ * Check key cardinality.
+ */
+- (void) checkKeyParts: (int) parts :(bool) partial_key_allowed;
+/**
+ * Unsafe search methods that do not check key cardinality.
+ */
+- (struct box_tuple *) findUnsafe: (void *) key :(int) key_cardinality;
+- (void) initIteratorUnsafe: (struct iterator *) iterator
+			:(enum iterator_type) type
+			:(void *) key :(int) parts;
 @end
 
 struct iterator {
diff --git a/mod/box/index.m b/mod/box/index.m
index 5d0e48cc2079a388aafb5767918b37f81682f497..799e99f6c0091dc6c2ff36fbbbc1e2c38587d973 100644
--- a/mod/box/index.m
+++ b/mod/box/index.m
@@ -131,10 +131,16 @@ iterator_first_equal(struct iterator *it)
 	return NULL;
 }
 
-- (struct box_tuple *) find: (void *) key :(int) key_cardinality
+- (struct box_tuple *) findByKey: (void *) key :(int) parts
+{
+	[self checkKeyParts: parts :false];
+	return [self findUnsafe: key :parts];
+}
+
+- (struct box_tuple *) findUnsafe: (void *) key :(int) parts
 {
 	(void) key;
-	(void) key_cardinality;
+	(void) parts;
 	[self subclassResponsibility: _cmd];
 	return NULL;
 }
@@ -173,16 +179,33 @@ iterator_first_equal(struct iterator *it)
 	[self subclassResponsibility: _cmd];
 }
 
-- (void) initIterator: (struct iterator *) iterator :(enum iterator_type) type
-                        :(void *) key
-			:(int) key_cardinality
+- (void) initIteratorByKey: (struct iterator *) iterator :(enum iterator_type) type
+                        :(void *) key :(int) parts
+{
+	[self checkKeyParts: parts :true];
+	[self initIteratorUnsafe: iterator :type :key :parts];
+}
+
+- (void) initIteratorUnsafe: (struct iterator *) iterator :(enum iterator_type) type
+                        :(void *) key :(int) parts
 {
 	(void) iterator;
 	(void) type;
 	(void) key;
-	(void) key_cardinality;
+	(void) parts;
 	[self subclassResponsibility: _cmd];
 }
+
+- (void) checkKeyParts: (int) parts :(bool) partial_key_allowed
+{
+	if (parts > key_def->part_count)
+		tnt_raise(ClientError, :ER_KEY_CARDINALITY,
+			  parts, key_def->part_count);
+	if (!partial_key_allowed && parts < key_def->part_count)
+		tnt_raise(ClientError, :ER_EXACT_MATCH,
+			  parts, key_def->part_count);
+}
+
 @end
 
 /* }}} */
@@ -279,7 +302,7 @@ hash_iterator_free(struct iterator *iterator)
 	if (field == NULL)
 		tnt_raise(ClientError, :ER_NO_SUCH_FIELD,
 			  key_def->parts[0].fieldno);
-	return [self find :field :1];
+	return [self findUnsafe :field :1];
 }
 
 - (struct iterator *) allocIterator
@@ -292,6 +315,14 @@ hash_iterator_free(struct iterator *iterator)
 	}
 	return (struct iterator *) it;
 }
+
+- (void) checkKeyParts: (int) parts :(bool) partial_key_allowed
+{
+	/* Hash indexes never allows partial keys. */
+	(void) partial_key_allowed;
+	[super checkKeyParts: parts :false];
+}
+
 @end
 
 /* }}} */
@@ -299,20 +330,11 @@ hash_iterator_free(struct iterator *iterator)
 /* {{{ Hash32Index ************************************************/
 
 static u32
-int32_key_to_value(void *key, int key_cardinality)
+int32_key_to_value(void *key)
 {
-	if (key_cardinality > 1)
-		tnt_raise(ClientError, :ER_KEY_CARDINALITY,
-			  key_cardinality, 1);
-
-	if (key_cardinality < 1)
-		tnt_raise(ClientError, :ER_EXACT_MATCH,
-			  key_cardinality, 1);
-
 	u32 key_size = load_varint32(&key);
 	if (key_size != 4)
 		tnt_raise(ClientError, :ER_KEY_FIELD_TYPE, "u32");
-
 	return *((u32 *) key);
 }
 
@@ -345,10 +367,12 @@ int32_key_to_value(void *key, int key_cardinality)
 	return mh_size(int_hash);
 }
 
-- (struct box_tuple *) find: (void *) key :(int) key_cardinality
+- (struct box_tuple *) findUnsafe: (void *) key :(int) parts
 {
+	(void) parts;
+
 	struct box_tuple *ret = NULL;
-	u32 num = int32_key_to_value(key, key_cardinality);
+	u32 num = int32_key_to_value(key);
 	mh_int_t k = mh_i32ptr_get(int_hash, num);
 	if (k != mh_end(int_hash))
 		ret = mh_value(int_hash, k);
@@ -361,7 +385,7 @@ int32_key_to_value(void *key, int key_cardinality)
 - (void) remove: (struct box_tuple *) tuple
 {
 	void *field = tuple_field(tuple, key_def->parts[0].fieldno);
-	u32 num = int32_key_to_value(field, 1);
+	u32 num = int32_key_to_value(field);
 	mh_int_t k = mh_i32ptr_get(int_hash, num);
 	if (k != mh_end(int_hash))
 		mh_i32ptr_del(int_hash, k);
@@ -374,7 +398,7 @@ int32_key_to_value(void *key, int key_cardinality)
 	:(struct box_tuple *) new_tuple
 {
 	void *field = tuple_field(new_tuple, key_def->parts[0].fieldno);
-	u32 num = int32_key_to_value(field, 1);
+	u32 num = int32_key_to_value(field);
 
 	if (old_tuple != NULL) {
 		void *old_field = tuple_field(old_tuple,
@@ -407,17 +431,17 @@ int32_key_to_value(void *key, int key_cardinality)
 	it->hash = int_hash;
 }
 
-- (void) initIterator: (struct iterator *) iterator :(enum iterator_type) type
-                        :(void *) key
-			:(int) key_cardinality
+- (void) initIteratorUnsafe: (struct iterator *) iterator :(enum iterator_type) type
+                        :(void *) key :(int) parts
 {
-	assert(iterator->next == hash_iterator_next);
-	struct hash_iterator *it = hash_iterator(iterator);
-
+	(void) parts;
 	if (type == ITER_REVERSE)
 		tnt_raise(IllegalParams, :"hash iterator is forward only");
 
-	u32 num = int32_key_to_value(key, key_cardinality);
+	assert(iterator->next == hash_iterator_next);
+	struct hash_iterator *it = hash_iterator(iterator);
+
+	u32 num = int32_key_to_value(key);
 	it->base.next_equal = iterator_first_equal;
 	it->h_pos = mh_i32ptr_get(int_hash, num);
 	it->hash = int_hash;
@@ -429,20 +453,11 @@ int32_key_to_value(void *key, int key_cardinality)
 /* {{{ Hash64Index ************************************************/
 
 static u64
-int64_key_to_value(void *key, int key_cardinality)
+int64_key_to_value(void *key)
 {
-	if (key_cardinality > 1)
-		tnt_raise(ClientError, :ER_KEY_CARDINALITY,
-			  key_cardinality, 1);
-
-	if (key_cardinality < 1)
-		tnt_raise(ClientError, :ER_EXACT_MATCH,
-			  key_cardinality, 1);
-
 	u32 key_size = load_varint32(&key);
 	if (key_size != 8)
 		tnt_raise(ClientError, :ER_KEY_FIELD_TYPE, "u64");
-
 	return *((u64 *) key);
 }
 
@@ -473,10 +488,12 @@ int64_key_to_value(void *key, int key_cardinality)
 	return mh_size(int64_hash);
 }
 
-- (struct box_tuple *) find: (void *) key :(int) key_cardinality
+- (struct box_tuple *) findUnsafe: (void *) key :(int) parts
 {
+	(void) parts;
+
 	struct box_tuple *ret = NULL;
-	u64 num = int64_key_to_value(key, key_cardinality);
+	u64 num = int64_key_to_value(key);
 	mh_int_t k = mh_i64ptr_get(int64_hash, num);
 	if (k != mh_end(int64_hash))
 		ret = mh_value(int64_hash, k);
@@ -489,7 +506,7 @@ int64_key_to_value(void *key, int key_cardinality)
 - (void) remove: (struct box_tuple *) tuple
 {
 	void *field = tuple_field(tuple, key_def->parts[0].fieldno);
-	u64 num = int64_key_to_value(field, 1);
+	u64 num = int64_key_to_value(field);
 
 	mh_int_t k = mh_i64ptr_get(int64_hash, num);
 	if (k != mh_end(int64_hash))
@@ -503,7 +520,7 @@ int64_key_to_value(void *key, int key_cardinality)
 	:(struct box_tuple *) new_tuple
 {
 	void *field = tuple_field(new_tuple, key_def->parts[0].fieldno);
-	u64 num = int64_key_to_value(field, 1);
+	u64 num = int64_key_to_value(field);
 
 	if (old_tuple != NULL) {
 		void *old_field = tuple_field(old_tuple,
@@ -535,17 +552,17 @@ int64_key_to_value(void *key, int key_cardinality)
 	it->hash = (struct mh_i32ptr_t *) int64_hash;
 }
 
-- (void) initIterator: (struct iterator *) iterator :(enum iterator_type) type
-                        :(void *) key
-			:(int) key_cardinality
+- (void) initIteratorUnsafe: (struct iterator *) iterator :(enum iterator_type) type
+                        :(void *) key :(int) parts
 {
-	assert(iterator->next == hash_iterator_next);
-	struct hash_iterator *it = hash_iterator(iterator);
-
+	(void) parts;
 	if (type == ITER_REVERSE)
 		tnt_raise(IllegalParams, :"hash iterator is forward only");
 
-	u64 num = int64_key_to_value(key, key_cardinality);
+	assert(iterator->next == hash_iterator_next);
+	struct hash_iterator *it = hash_iterator(iterator);
+
+	u64 num = int64_key_to_value(key);
 
 	it->base.next_equal = iterator_first_equal;
 	it->h_pos = mh_i64ptr_get(int64_hash, num);
@@ -557,18 +574,6 @@ int64_key_to_value(void *key, int key_cardinality)
 
 /* {{{ HashStrIndex ***********************************************/
 
-static char *
-str_key_to_value(void *key, int key_cardinality)
-{
-	if (key_cardinality > 1)
-		tnt_raise(ClientError, :ER_KEY_CARDINALITY,
-			  key_cardinality, 1);
-	if (key_cardinality < 1)
-		tnt_raise(ClientError, :ER_EXACT_MATCH,
-			  key_cardinality, 1);
-	return key;
-}
-
 @interface HashStrIndex: HashIndex {
 	 struct mh_lstrptr_t *str_hash;
 };
@@ -596,11 +601,11 @@ str_key_to_value(void *key, int key_cardinality)
 	return mh_size(str_hash);
 }
 
-- (struct box_tuple *) find: (void *) key :(int) key_cardinality
+- (struct box_tuple *) findUnsafe: (void *) key :(int) parts
 {
+	(void) parts;
 	struct box_tuple *ret = NULL;
-	void *lstr = str_key_to_value(key, key_cardinality);
-	mh_int_t k = mh_lstrptr_get(str_hash, lstr);
+	mh_int_t k = mh_lstrptr_get(str_hash, key);
 	if (k != mh_end(str_hash))
 		ret = mh_value(str_hash, k);
 #ifdef DEBUG
@@ -663,19 +668,19 @@ str_key_to_value(void *key, int key_cardinality)
 	it->hash = (struct mh_i32ptr_t *) str_hash;
 }
 
-- (void) initIterator: (struct iterator *) iterator :(enum iterator_type) type
-                        :(void *) key
-			:(int) key_cardinality
+- (void) initIteratorUnsafe: (struct iterator *) iterator
+			:(enum iterator_type) type
+                        :(void *) key :(int) parts
 {
-	assert(iterator->next == hash_iterator_next);
-	struct hash_iterator *it = hash_iterator(iterator);
-
+	(void) parts;
 	if (type == ITER_REVERSE)
 		tnt_raise(IllegalParams, :"hash iterator is forward only");
 
-	void *lstr = str_key_to_value(key, key_cardinality);
+	assert(iterator->next == hash_iterator_next);
+	struct hash_iterator *it = hash_iterator(iterator);
+
 	it->base.next_equal = iterator_first_equal;
-	it->h_pos = mh_lstrptr_get(str_hash, lstr);
+	it->h_pos = mh_lstrptr_get(str_hash, key);
 	it->hash = (struct mh_i32ptr_t *) str_hash;
 }
 @end
diff --git a/mod/box/memcached.m b/mod/box/memcached.m
index 9e2deb1c97ff67c7185f1e4be58ef5969dc6ef4e..3669d19eff5dd719eac7252901f343e78a253c3f 100644
--- a/mod/box/memcached.m
+++ b/mod/box/memcached.m
@@ -131,7 +131,7 @@ delete(void *key)
 static struct box_tuple *
 find(void *key)
 {
-	return [memcached_index find :key :1];
+	return [memcached_index findByKey :key :1];
 }
 
 static struct meta *
diff --git a/mod/box/tree.m b/mod/box/tree.m
index 76d589e72877330c7ef96bd1ecb5a4bc5a4e520e..78e1e13615920eb03bbf6eb291b629515812b34d 100644
--- a/mod/box/tree.m
+++ b/mod/box/tree.m
@@ -864,22 +864,14 @@ tree_iterator_free(struct iterator *iterator)
 	return [self unfold: node];
 }
 
-- (struct box_tuple *) find: (void *) key : (int) key_cardinality
+- (struct box_tuple *) findUnsafe: (void *) key : (int) parts
 {
 	struct key_data *key_data
 		= alloca(sizeof(struct key_data) +
-			 _SIZEOF_SPARSE_PARTS(key_cardinality));
-
-	if (key_cardinality > key_def->part_count)
-		tnt_raise(ClientError, :ER_KEY_CARDINALITY,
-			  key_cardinality, key_def->part_count);
-
-	if (key_cardinality < key_def->part_count)
-		tnt_raise(ClientError, :ER_EXACT_MATCH,
-			  key_cardinality, key_def->part_count);
+			 _SIZEOF_SPARSE_PARTS(parts));
 
 	key_data->data = key;
-	key_data->part_count = key_cardinality;
+	key_data->part_count = parts;
 	fold_with_key_parts(key_def, key_data);
 
 	void *node = sptree_index_find(&tree, key_data);
@@ -938,22 +930,17 @@ tree_iterator_free(struct iterator *iterator)
 
 - (void) initIterator: (struct iterator *) iterator :(enum iterator_type) type
 {
-	[self initIterator: iterator :type :NULL :0];
+	[self initIteratorUnsafe: iterator :type :NULL :0];
 }
 
-- (void) initIterator: (struct iterator *) iterator :(enum iterator_type) type
-                        :(void *) key
-			:(int) key_cardinality
+- (void) initIteratorUnsafe: (struct iterator *) iterator :(enum iterator_type) type
+                        :(void *) key :(int) parts
 {
 	assert(iterator->free == tree_iterator_free);
 	struct tree_iterator *it = tree_iterator(iterator);
 
-	if (key_cardinality > key_def->part_count)
-		tnt_raise(ClientError, :ER_KEY_CARDINALITY,
-			  key_cardinality, key_def->part_count);
-
 	it->key_data.data = key;
-	it->key_data.part_count = key_cardinality;
+	it->key_data.part_count = parts;
 	fold_with_key_parts(key_def, &it->key_data);
 
 	if (type == ITER_FORWARD) {