MongoDB возвращает последний полный документ для каждого идентификатора (Полный документ Объект, содержащий все поля, такие как массивы вспомогательных документов и т. Д.) [Duplicate]

Чтобы предложить лучшее решение, я могу сказать, что обнаружил следующий метод:

parseFloat((0.1 + 0.2).toFixed(10)) => Will return 0.3

Позвольте мне объяснить, почему это лучшее решение. Как упоминалось выше в других ответах, рекомендуется использовать готовые для использования функции Javascript toFixed () для решения проблемы. Но, скорее всего, вы столкнетесь с некоторыми проблемами.

Представьте, что вы собираетесь добавить два числа с плавающей запятой, такие как 0.2 и 0.7, вот оно: 0.2 + 0.7 = 0.8999999999999999.

Ваш ожидаемый результат 0.9 означает, что в этом случае вам нужен результат с точностью до 1 цифры. Поэтому вы должны были использовать (0.2 + 0.7).tofixed(1), но вы не можете просто указать определенный параметр toFixed (), поскольку он зависит от заданного числа, например

`0.22 + 0.7 = 0.9199999999999999`

. В этом примере вам нужна точность в 2 цифры так что это должно быть toFixed(2), так что должно быть параметром для каждого заданного числа с плавающей запятой?

Вы могли бы сказать, что пусть это будет 10 в каждой ситуации:

(0.2 + 0.7).toFixed(10) => Result will be "0.9000000000"

Черт! Что вы собираетесь делать с этими нежелательными нулями после 9? Пришло время преобразовать его в float, чтобы сделать его по вашему желанию:

parseFloat((0.2 + 0.7).toFixed(10)) => Result will be 0.9

Теперь, когда вы нашли решение, лучше предложить его как функцию:

function floatify(number){
       return parseFloat((number).toFixed(10));
    }
 
function addUp(){
  var number1 = +$("#number1").val();
  var number2 = +$("#number2").val();
  var unexpectedResult = number1 + number2;
  var expectedResult = floatify(number1 + number2);
  $("#unexpectedResult").text(unexpectedResult);
  $("#expectedResult").text(expectedResult);
}
addUp();
input{
  width: 50px;
}
#expectedResult{
color: green;
}
#unexpectedResult{
color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="number1" value="0.2" onclick="addUp()" onkeyup="addUp()"/> +
<input id="number2" value="0.7" onclick="addUp()" onkeyup="addUp()"/> =
<p>Expected Result: <span id="expectedResult"></span></p>
<p>Unexpected Result: <span id="unexpectedResult"></span></p>

Вы можете использовать его следующим образом:

var x = 0.2 + 0.7;
floatify(x);  => Result: 0.9
7
задан Blakes Seven 11 March 2016 в 04:33
поделиться

3 ответа

Чтобы ответить на ваш вопрос, да, это самый эффективный способ. Но я думаю, что нам нужно выяснить, почему это так.

Как было предложено в альтернативах, люди, на которые люди смотрят, «сортируют» ваши результаты, прежде чем перейти на стадию $group и что они смотрят на значение «timestamp», поэтому вы хотите убедиться, что все находится в порядке «timestamp», поэтому отсюда:

db.temperature.aggregate([
    { "$sort": { "station": 1, "dt": -1 } },
    { "$group": {
        "_id": "$station", 
        "result": { "$first":"$dt"}, "t": {"$first":"$t"} 
    }}
])

И, как указано, вы, конечно, захотите индекс, чтобы отразить это, чтобы сделать сортировку эффективной:

Однако, и это реальная точка. То, что, по-видимому, было упущено другими (если не так для себя), состоит в том, что все эти данные, вероятно, будут вставлены уже в порядке времени, поскольку каждое чтение записывается как добавленное.

Таким образом, красота этого это поле _id (с по умолчанию ObjectId) уже находится в порядке «timestamp», поскольку оно фактически содержит значение времени, и это делает возможным утверждение:

db.temperature.aggregate([
    { "$group": {
        "_id": "$station", 
        "result": { "$last":"$dt"}, "t": {"$last":"$t"} 
    }}
])

И это быстрее. Зачем? Ну, вам не нужно выбирать индекс (дополнительный код для вызова), вам также не нужно «загружать» индекс в дополнение к документу.

Мы уже знаем, что документы в порядке ( на _id), поэтому границы $last совершенно верны. Вы все равно сканируете все, и вы также можете «задавать» запрос по значениям _id одинаково допустимым для двух дат.

Единственное, что можно сказать здесь, это то, что в «реальном мире», использование, это может быть более практичным для вас $match между диапазонами дат при выполнении такого рода накопления в отличие от получения значений «первый» и «последний» _id для определения «диапазона» или чего-то подобного в ваше фактическое использование.

Итак, где доказательство этого? Ну, это довольно легко воспроизвести, поэтому я просто сделал это, создав некоторые примеры данных:

var stations = [ 
    "AL", "AK", "AZ", "AR", "CA", "CO", "CT", "DE", "FL",
    "GA", "HI", "ID", "IL", "IN", "IA", "KS", "KY", "LA",
    "ME", "MD", "MA", "MI", "MN", "MS", "MO", "MT", "NE",
    "NV", "NH", "NJ", "NM", "NY", "NC", "ND", "OH", "OK",
    "OR", "PA", "RI", "SC", "SD", "TN", "TX", "UT", "VT",
    "VA", "WA", "WV", "WI", "WY"
];


for ( i=0; i<200000; i++ ) {

    var station = stations[Math.floor(Math.random()*stations.length)];
    var t = Math.floor(Math.random() * ( 96 - 50 + 1 )) +50;
    dt = new Date();

    db.temperatures.insert({
        station: station,
        t: t,
        dt: dt
    });

}

На моем оборудовании (ноутбук 8 ГБ со спиннивным диском, который не является звездным, но, безусловно, адекватным) форма заявления четко показывает заметную паузу с версией, используя индекс и сортировку (те же ключи в индексе, что и оператор сортировки). Это лишь незначительная пауза, но разница достаточно значительна, чтобы заметить.

Даже глядя на вывод объяснения (версия 2.6 и выше, или на самом деле есть в 2.4.9, хотя и не задокументирована), вы можете видеть разница в этом, хотя $sort оптимизирована из-за наличия индекса, время, которое, как представляется, относится к выбору индекса, а затем загружает индексированные записи. Включение всех полей для «охватываемых» запросов индекса не имеет значения.

Также для записи чисто индексация даты и только сортировка по значениям даты дают тот же результат. Возможно, немного быстрее, но все же медленнее, чем форма естественного индекса без сортировки.

Так что до тех пор, пока вы можете с радостью «диапазоном» на сначала и последним _id, то верно, что использование естественного индекса в порядке вставки фактически является наиболее эффективным способом сделать это. Ваш реальный пробег в мире может варьироваться в зависимости от того, насколько это практично для вас или нет, и это может оказаться более удобным для реализации индекса и сортировки по дате.

Но если вы были довольны использованием _id или больше, чем «последний» _id в вашем запросе, а затем, возможно, одна настройка, чтобы получить значения вместе с вашими результатами, чтобы вы могли хранить и использовать эту информацию в следующих запросах:

db.temperature.aggregate([
    // Get documents "greater than" the "highest" _id value found last time
    { "$match": {
        "_id": { "$gt":  ObjectId("536076603e70a99790b7845d") }
    }},

    // Do the grouping with addition of the returned field
    { "$group": {
        "_id": "$station", 
        "result": { "$last":"$dt"},
        "t": {"$last":"$t"},
        "lastDoc": { "$last": "$_id" } 
    }}
])

И если вы на самом деле «следили» за такими результатами, вы можете определить максимальное значение ObjectId из ваших результатов и использовать его в следующем запросе.

Во всяком случае, получайте удовольствие играя с этим, но опять же Да, в этом случае этот запрос является самым быстрым способом.

6
ответ дан Neil Lunn 28 August 2018 в 22:29
поделиться

Индекс - это все, что вам действительно нужно:

db.temperature.ensureIndex({ 'station': 1, 'dt': 1 })
for s in db.temperature.distinct('station'):
    db.temperature.find({ station: s }).sort({ dt : -1 }).limit(1)

, конечно, используя любой синтаксис, действительно действительный для вашего языка.

Edit: Вы правы, что такой цикл берет на себя поездку на одну поездку, и это отлично подходит для нескольких станций, и не очень хорошо для 1000. Однако вы все же хотите использовать составной индекс на станции + dt и использовать нисходящую сортировку:

db.temperature.aggregate([
    { $sort: { station: 1, dt: -1 } },
    { $group: { _id: "$station", result: {$first:"$dt"}, t: {$first:"$t"} } }
])
2
ответ дан Ben Gamble 28 August 2018 в 22:29
поделиться

Что касается запроса агрегирования, который вы опубликовали, я бы удостоверился, что у вас есть индекс на dt:

db.temperature.ensureIndex({'dt': 1 })

. Это будет гарантировать, что сортировка $ в начале агрегатный трубопровод максимально эффективен.

Что касается того, является ли это наиболее эффективным способом получения этих данных, а также запросом в цикле, скорее всего, будет функция того, сколько у вас данных. В начале, с «тысячами станций» и, возможно, сотнями тысяч точек данных, я думаю, что метод агрегирования будет быстрее.

Однако, поскольку вы добавляете все больше и больше данных, проблема заключается в том, что запрос агрегации будет по-прежнему касаться всех документов. Это будет становиться все более дорогостоящим, поскольку вы масштабируетесь до миллионов или более документов. Одним из подходов для этого случая было бы добавить $ limit сразу после сортировки $, чтобы ограничить общее количество рассматриваемых документов. Это немного взломанно и неточно, но это поможет ограничить общее количество документов, к которым необходимо получить доступ.

1
ответ дан John Petrone 28 August 2018 в 22:29
поделиться
Другие вопросы по тегам:

Похожие вопросы: