Это следующее:
class C(object):
def __init__(self):
self._x = None
@property
def x(self):
"""I'm the 'x' property."""
return self._x
@x.setter
def x(self, value):
self._x = value
@x.deleter
def x(self):
del self._x
То же, что и:
class C(object):
def __init__(self):
self._x = None
def _x_get(self):
return self._x
def _x_set(self, value):
self._x = value
def _x_del(self):
del self._x
x = property(_x_get, _x_set, _x_del,
"I'm the 'x' property.")
То же, что и:
class C(object):
def __init__(self):
self._x = None
def _x_get(self):
return self._x
def _x_set(self, value):
self._x = value
def _x_del(self):
del self._x
x = property(_x_get, doc="I'm the 'x' property.")
x = x.setter(_x_set)
x = x.deleter(_x_del)
То же, что и :
class C(object):
def __init__(self):
self._x = None
def _x_get(self):
return self._x
x = property(_x_get, doc="I'm the 'x' property.")
def _x_set(self, value):
self._x = value
x = x.setter(_x_set)
def _x_del(self):
del self._x
x = x.deleter(_x_del)
Это то же самое, что:
class C(object):
def __init__(self):
self._x = None
@property
def x(self):
"""I'm the 'x' property."""
return self._x
@x.setter
def x(self, value):
self._x = value
@x.deleter
def x(self):
del self._x
Запрос, который вы хотите, следующий:
db.collection.find({"users":{"$not":{"$elemMatch":{"user":{$nin:[1,5,7]}}}}})
Это говорит, что найти все документы, которые не имеют элементов, которые находятся за пределами списка 1,5,7.
Я просто потратил значительную часть своего времени на то, чтобы попытаться реализовать решение Асии выше с объектными сравнениями, а не строгое равенство. Поэтому я решил поделиться этим здесь.
Допустим, вы расширили свой вопрос от userIds до полных пользователей. Вы хотите найти все документы, в которых каждый элемент в массиве users
присутствует в другом массиве пользователей: [{user: 1, group: 3}, {user: 2, group: 5},...]
Это не сработает: db.collection.find({"users":{"$not":{"$elemMatch":{"$nin":[{user: 1, group: 3},{user: 2, group: 5},...]}}}}})
, потому что $ nin работает только для строгого равенства. Поэтому нам нужно найти другой способ выражения «Не в массиве» для массивов объектов. И использование $where
слишком сильно замедлит запрос.
Решение:
db.collection.find({
"users": {
"$not": {
"$elemMatch": {
// if all of the OR-blocks are true, element is not in array
"$and": [{
// each OR-block == true if element != that user
"$or": [
"user": { "ne": 1 },
"group": { "ne": 3 }
]
}, {
"$or": [
"user": { "ne": 2 },
"group": { "ne": 5 }
]
}, {
// more users...
}]
}
}
}
})
Чтобы завершить логику: $ elemMatch соответствует всем документам, у которых есть пользователь, который не находится в массив. Таким образом, $ не будет соответствовать всем документам, в которых есть все пользователи в массиве.
Я не знаю о лучшем, но есть несколько разных способов приблизиться к этому, и в зависимости от версии MongoDB у вас есть.
Не слишком уверен, что это ваше намерение или нет , но запрос, как показано, будет соответствовать первому примеру документа, поскольку, поскольку ваша логика реализована, вы соответствуете элементам в массиве этого документа, которые должны содержаться в массиве образцов.
Так что, если вы действительно хотели документ чтобы содержать все эти элементы, тогда оператор $all
был бы очевидным выбором:
db.collection.find({ "users.user": { "$all": [ 1, 5, 7 ] } })
Но работая с предположением о том, что ваша логика на самом деле предназначена, по крайней мере в соответствии с предложением вы можете «фильтровать» эти результаты, комбинируя с оператором $in
, чтобы на вашем было меньше документов $where
** условие в оценке JavaScript:
db.collection.find({
"users.user": { "$in": [ 1, 5, 7 ] },
"$where": function() {
var ids = [1, 5, 7];
return this.users.every(function(u) {
return ids.indexOf(u.user) !== -1;
});
}
})
И вы получаете индекс, хотя фактический отсканированный будет умножен на количество элементов в массивах из согласованного документа но еще лучше, чем без дополнительного фильтра.
Или, возможно, вы рассматриваете логическую абстракцию оператора $and
, используемого в комбинации с $or
и, возможно, оператор $size
в зависимости от ваших реальных условий массива:
db.collection.find({
"$or": [
{ "users.user": { "$all": [ 1, 5, 7 ] } },
{ "users.user": { "$all": [ 1, 5 ] } },
{ "users.user": { "$all": [ 1, 7 ] } },
{ "users": { "$size": 1 }, "users.user": 1 },
{ "users": { "$size": 1 }, "users.user": 5 },
{ "users": { "$size": 1 }, "users.user": 7 }
]
})
Итак, это поколение всех возможных перестановок вашего условия соответствия, но опять-таки производительность, скорее всего, будет зависеть от вашей доступной установленной версии.
ПРИМЕЧАНИЕ. На самом деле полный сбой в этом случае, поскольку это делает что-то совершенно другое и фактически приводит к логическому $in
Альтернативы с каркасом агрегации, ваш пробег может отличаться от того, что наиболее эффективно из-за количества документов в вашей коллекции, одного подхода с MongoDB 2.6 и выше:
db.problem.aggregate([
// Match documents that "could" meet the conditions
{ "$match": {
"users.user": { "$in": [ 1, 5, 7 ] }
}},
// Keep your original document and a copy of the array
{ "$project": {
"_id": {
"_id": "$_id",
"date": "$date",
"users": "$users"
},
"users": 1,
}},
// Unwind the array copy
{ "$unwind": "$users" },
// Just keeping the "user" element value
{ "$group": {
"_id": "$_id",
"users": { "$push": "$users.user" }
}},
// Compare to see if all elements are a member of the desired match
{ "$project": {
"match": { "$setEquals": [
{ "$setIntersection": [ "$users", [ 1, 5, 7 ] ] },
"$users"
]}
}},
// Filter out any documents that did not match
{ "$match": { "match": true } },
// Return the original document form
{ "$project": {
"_id": "$_id._id",
"date": "$_id.date",
"users": "$_id.users"
}}
])
So этот подход использует некоторые недавно введенные операторы set , чтобы сравнить содержимое, хотя, конечно, вам нужно реструктурировать массив, чтобы сделать е. сравнение.
Как указывалось, в $setIsSubset
имеется прямой оператор, который делает эквивалент комбинированных операторов выше в одном операторе:
db.collection.aggregate([
{ "$match": {
"users.user": { "$in": [ 1,5,7 ] }
}},
{ "$project": {
"_id": {
"_id": "$_id",
"date": "$date",
"users": "$users"
},
"users": 1,
}},
{ "$unwind": "$users" },
{ "$group": {
"_id": "$_id",
"users": { "$push": "$users.user" }
}},
{ "$project": {
"match": { "$setIsSubset": [ "$users", [ 1, 5, 7 ] ] }
}},
{ "$match": { "match": true } },
{ "$project": {
"_id": "$_id._id",
"date": "$_id.date",
"users": "$_id.users"
}}
])
Или с другим подходом, все еще использующим оператор $size
от MongoDB 2.6:
db.collection.aggregate([
// Match documents that "could" meet the conditions
{ "$match": {
"users.user": { "$in": [ 1, 5, 7 ] }
}},
// Keep your original document and a copy of the array
// and a note of it's current size
{ "$project": {
"_id": {
"_id": "$_id",
"date": "$date",
"users": "$users"
},
"users": 1,
"size": { "$size": "$users" }
}},
// Unwind the array copy
{ "$unwind": "$users" },
// Filter array contents that do not match
{ "$match": {
"users.user": { "$in": [ 1, 5, 7 ] }
}},
// Count the array elements that did match
{ "$group": {
"_id": "$_id",
"size": { "$first": "$size" },
"count": { "$sum": 1 }
}},
// Compare the original size to the matched count
{ "$project": {
"match": { "$eq": [ "$size", "$count" ] }
}},
// Filter out documents that were not the same
{ "$match": { "match": true } },
// Return the original document form
{ "$project": {
"_id": "$_id._id",
"date": "$_id.date",
"users": "$_id.users"
}}
])
Что, конечно, все еще можно сделать , хотя немного более длинные в версиях до 2.6:
db.collection.aggregate([
// Match documents that "could" meet the conditions
{ "$match": {
"users.user": { "$in": [ 1, 5, 7 ] }
}},
// Keep your original document and a copy of the array
{ "$project": {
"_id": {
"_id": "$_id",
"date": "$date",
"users": "$users"
},
"users": 1,
}},
// Unwind the array copy
{ "$unwind": "$users" },
// Group it back to get it's original size
{ "$group": {
"_id": "$_id",
"users": { "$push": "$users" },
"size": { "$sum": 1 }
}},
// Unwind the array copy again
{ "$unwind": "$users" },
// Filter array contents that do not match
{ "$match": {
"users.user": { "$in": [ 1, 5, 7 ] }
}},
// Count the array elements that did match
{ "$group": {
"_id": "$_id",
"size": { "$first": "$size" },
"count": { "$sum": 1 }
}},
// Compare the original size to the matched count
{ "$project": {
"match": { "$eq": [ "$size", "$count" ] }
}},
// Filter out documents that were not the same
{ "$match": { "match": true } },
// Return the original document form
{ "$project": {
"_id": "$_id._id",
"date": "$_id.date",
"users": "$_id.users"
}}
])
Это обычно округляет разные способы, пробует их и видит, что лучше всего подходит для вас. По всей вероятности, простая комбинация $in
с вашей существующей формой, вероятно, будет лучшей. Но во всех случаях убедитесь, что у вас есть индекс, который можно выбрать:
db.collection.ensureIndex({ "users.user": 1 })
. Это даст вам лучшую производительность, если вы каким-то образом получаете доступ к этому, так как все примеры здесь.
Я был заинтригован этим, поэтому в конечном итоге наработал тестовый пример, чтобы узнать, что было лучше всего. Итак, сначала некоторые генерации тестовых данных:
var batch = [];
for ( var n = 1; n <= 10000; n++ ) {
var elements = Math.floor(Math.random(10)*10)+1;
var obj = { date: new Date(), users: [] };
for ( var x = 0; x < elements; x++ ) {
var user = Math.floor(Math.random(10)*10)+1,
group = Math.floor(Math.random(10)*10)+1;
obj.users.push({ user: user, group: group });
}
batch.push( obj );
if ( n % 500 == 0 ) {
db.problem.insert( batch );
batch = [];
}
}
С 10000 документами в коллекции со случайными массивами от 1..10 в длине, содержащими случайные значения 1..0, я пришел к числу совпадений 430 документы (уменьшены с 7749 по совпадению $in
) со следующими результатами (avg):
$in
: 420 мс $size
: 395ms $setIsSubset
: 250 мс Отмечая, что по образцам, выполненным все, кроме последних двух, была дисперсия peak примерно на 100 мс быстрее, а последние два показали 220 мс ответ. Наибольшие вариации были в запросе JavaScript, который также показал результаты на 100 мс медленнее.
Но дело здесь в том, что касается аппаратного обеспечения, которое на моем ноутбуке под VM не особенно велико, но дает представление.
Таким образом, совокупность и, в частности, версия MongoDB 2.6.1 с установленными операторами явно выигрывают от производительности с дополнительным небольшим усилением, поступающим от $setIsSubset
как один оператор.
Это особенно интересно (как указано в методе, совместимом с 2.4) наибольшая стоимость этого процесса будет заключаться в заявлении $unwind
(более 100 мс), поэтому при выборе $in
, имеющем среднее значение около 32 мс, остальные этапы конвейера выполняются меньше чем 100 мс в среднем. Таким образом, это дает относительное представление об агрегации и производительности JavaScript.
users
либо отсутствует, либо пуст. – JohnnyHK 13 May 2014 в 13:29{"users.user":{$exists:true}}
– Asya Kamsky 13 May 2014 в 19:00