Когда вы объявляете ссылочную переменную (т. е. объект), вы действительно создаете указатель на объект. Рассмотрим следующий код, в котором вы объявляете переменную примитивного типа int
:
int x;
x = 10;
В этом примере переменная x является int
, и Java инициализирует ее для 0. Когда вы назначаете его 10 во второй строке, ваше значение 10 записывается в ячейку памяти, на которую указывает x.
Но когда вы пытаетесь объявить ссылочный тип, произойдет что-то другое. Возьмите следующий код:
Integer num;
num = new Integer(10);
Первая строка объявляет переменную с именем num
, но она не содержит примитивного значения. Вместо этого он содержит указатель (потому что тип Integer
является ссылочным типом). Поскольку вы еще не указали, что указать на Java, он устанавливает значение null, что означает «Я ничего не указываю».
Во второй строке ключевое слово new
используется для создания экземпляра (или создания ) объекту типа Integer и переменной указателя num
присваивается этот объект. Теперь вы можете ссылаться на объект, используя оператор разыменования .
(точка).
Exception
, о котором вы просили, возникает, когда вы объявляете переменную, но не создавали объект. Если вы попытаетесь разыменовать num
. Перед созданием объекта вы получите NullPointerException
. В самых тривиальных случаях компилятор поймает проблему и сообщит вам, что «num не может быть инициализирован», но иногда вы пишете код, который непосредственно не создает объект.
Например, вы можете имеют следующий метод:
public void doSomething(SomeObject obj) {
//do something to obj
}
В этом случае вы не создаете объект obj
, скорее предполагая, что он был создан до вызова метода doSomething
. К сожалению, этот метод можно вызвать следующим образом:
doSomething(null);
В этом случае obj
имеет значение null. Если метод предназначен для того, чтобы что-то сделать для переданного объекта, целесообразно бросить NullPointerException
, потому что это ошибка программиста, и программисту понадобится эта информация для целей отладки.
Альтернативно, там могут быть случаи, когда цель метода заключается не только в том, чтобы работать с переданным в объекте, и поэтому нулевой параметр может быть приемлемым. В этом случае вам нужно будет проверить нулевой параметр и вести себя по-другому. Вы также должны объяснить это в документации. Например, doSomething
может быть записано как:
/**
* @param obj An optional foo for ____. May be null, in which case
* the result will be ____.
*/
public void doSomething(SomeObject obj) {
if(obj != null) {
//do something
} else {
//do something else
}
}
Наконец, Как определить исключение & amp; причина использования Трассировки стека
Запрос, который вы хотите, следующий:
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