Как преобразовать строку в числовые значения в mongodb
Я пытаюсь преобразовать строку, содержащую числовое значение, в ее значение в агрегированном запросе в MongoDB.
Пример документа
{
"_id": ObjectId("5522XXXXXXXXXXXX"),
"Date": "2015-04-05",
"PartnerID": "123456",
"moop": "1234"
}
Пример агрегированного запроса, который я использую
{
aggregate: 'my_collection',
pipeline: [
{$match: {
Date :
{$gt:'2015-04-01',
$lt: '2015-04-05'
}}
},
{$group:
{_id: "$PartnerID",
total:{$sum:'$moop'}
}}]}
где результаты
{
"result": [
{
"_id": "123456",
"total": NumberInt(0)
}
}
Как вы можете преобразовать строку в числовое значение?
Ответы
Ответ 1
У агрегации MongoDB не разрешено изменять существующий тип данных заданных полей. В этом случае вы должны создать некоторый программный код для преобразования string
в int
. Проверьте ниже код
db.collectionName.find().forEach(function(data) {
db.collectionName.update({
"_id": data._id,
"moop": data.moop
}, {
"$set": {
"PartnerID": parseInt(data.PartnerID)
}
});
})
Если размер вашей коллекции больше, чем выше script, замедлит производительность, для perfomace mongo обеспечьте mongo bulk, используя операции массового массового обслуживания также обновленный тип данных
var bulk = db.collectionName.initializeOrderedBulkOp();
var counter = 0;
db.collectionName.find().forEach(function(data) {
var updoc = {
"$set": {}
};
var myKey = "PartnerID";
updoc["$set"][myKey] = parseInt(data.PartnerID);
// queue the update
bulk.find({
"_id": data._id
}).update(updoc);
counter++;
// Drain and re-initialize every 1000 update statements
if (counter % 1000 == 0) {
bulk.execute();
bulk = db.collectionName.initializeOrderedBulkOp();
}
})
// Add the rest in the queue
if (counter % 1000 != 0) bulk.execute();
Это в основном уменьшает количество операторов операций, отправленных на сервер, только для отправки только каждые 1000 операций в очереди.
Ответ 2
Вы можете легко преобразовать строковый тип данных в числовой тип данных.
Не забудьте изменить collectionName & FieldName. например: CollectionNmae: Users & FieldName: Contactno.
Попробуйте этот запрос..
db.collectionName.find().forEach( function (x) {
x.FieldName = parseInt(x.FieldName);
db.collectionName.save(x);
});
Ответ 3
В конце концов я использовал
db.my_collection.find({moop: {$exists: true}}).forEach(function(obj) {
obj.moop = new NumberInt(obj.moop);
db.my_collection.save(obj);
});
превратить moop
из строки в целое в my_collection, следуя примеру ответа Simone. MongoDB: Как изменить тип поля? ,
Ответ 4
Использование MongoDB 4.0 и новее
У вас есть два варианта: $toInt
или $convert
. Используя $toInt
, следуйте примеру ниже:
filterDateStage = {
'$match': {
'Date': {
'$gt': '2015-04-01',
'$lt': '2015-04-05'
}
}
};
groupStage = {
'$group': {
'_id': '$PartnerID',
'total': { '$sum': { '$toInt': '$moop' } }
}
};
db.getCollection('my_collection').aggregate([
filterDateStage,
groupStage
])
Если операция преобразования обнаруживает ошибку, операция агрегирования останавливается и выдает ошибку. Чтобы переопределить это поведение, используйте вместо этого $convert
.
Использование $convert
groupStage = {
'$group': {
'_id': '$PartnerID',
'total': {
'$sum': {
'$convert': { 'input': '$moop', 'to': 'int' }
}
}
}
};
Использование Map/Reduce
С map/lower вы можете использовать функции javascript, такие как parseInt()
чтобы сделать преобразование. Например, вы можете определить функцию карты для обработки каждого входного документа: в функции this
относится к документу, который обрабатывает операция уменьшения карты. Функция отображает преобразованное moop
строки moop
в PartnerID
для каждого документа и генерирует PartnerID
и преобразованную пару moop
. Вот где можно применять встроенную функцию javascript parseInt()
:
var mapper = function () {
var x = parseInt(this.moop);
emit(this.PartnerID, x);
};
Затем определите соответствующую функцию keyCustId
с двумя аргументами keyCustId
и valuesMoop
. valuesMoop
- это массив, элементами которого являются целочисленные значения moop
выводятся функцией map и группируются по keyPartnerID
. Функция уменьшает массив valuesMoop
до суммы его элементов.
var reducer = function(keyPartnerID, valuesMoop) {
return Array.sum(valuesMoop);
};
db.collection.mapReduce(
mapper,
reducer,
{
out : "example_results",
query: {
Date: {
$gt: "2015-04-01",
$lt: "2015-04-05"
}
}
}
);
db.example_results.find(function (err, docs) {
if(err) console.log(err);
console.log(JSON.stringify(docs));
});
Например, со следующим образцом коллекции документов:
/* 0 */
{
"_id" : ObjectId("550c00f81bcc15211016699b"),
"Date" : "2015-04-04",
"PartnerID" : "123456",
"moop" : "1234"
}
/* 1 */
{
"_id" : ObjectId("550c00f81bcc15211016699c"),
"Date" : "2015-04-03",
"PartnerID" : "123456",
"moop" : "24"
}
/* 2 */
{
"_id" : ObjectId("550c00f81bcc15211016699d"),
"Date" : "2015-04-02",
"PartnerID" : "123457",
"moop" : "21"
}
/* 3 */
{
"_id" : ObjectId("550c00f81bcc15211016699e"),
"Date" : "2015-04-02",
"PartnerID" : "123457",
"moop" : "8"
}
Приведенная выше операция Map/Reduce сохранит результаты в коллекцию example_results
а команда оболочки db.example_results.find()
даст:
/* 0 */
{
"_id" : "123456",
"value" : 1258
}
/* 1 */
{
"_id" : "123457",
"value" : 29
}
Ответ 5
Строка может быть преобразована в числа в MongoDB v4.0 с использованием оператора $ toInt. В этом случае
db.col.aggregate([
{
$project: {
_id: 0,
moopNumber: { $toInt: "$moop" }
}
}
])
выходы:
{ "moopNumber" : 1234 }
Ответ 6
Вот чистое решение на основе MongoDB для этой проблемы, которое я просто написал для удовольствия. Это эффективно серверный синтаксический анализатор строк и чисел, который поддерживает положительные и отрицательные числа, а также десятичные числа:
db.collection.aggregate({
$addFields: {
"moop": {
$reduce: {
"input": {
$map: { // split string into char array so we can loop over individual characters
"input": {
$range: [ 0, { $strLenCP: "$moop" } ] // using an array of all numbers from 0 to the length of the string
},
"in":{
$substrCP: [ "$moop", "$$this", 1 ] // return the nth character as the mapped value for the current index
}
}
},
"initialValue": { // initialize the parser with a 0 value
"n": 0, // the current number
"sign": 1, // used for positive/negative numbers
"div": null, // used for shifting on the right side of the decimal separator "."
"mult": 10 // used for shifting on the left side of the decimal separator "."
}, // start with a zero
"in": {
$let: {
"vars": {
"n": {
$switch: { // char-to-number mapping
branches: [
{ "case": { $eq: [ "$$this", "1" ] }, "then": 1 },
{ "case": { $eq: [ "$$this", "2" ] }, "then": 2 },
{ "case": { $eq: [ "$$this", "3" ] }, "then": 3 },
{ "case": { $eq: [ "$$this", "4" ] }, "then": 4 },
{ "case": { $eq: [ "$$this", "5" ] }, "then": 5 },
{ "case": { $eq: [ "$$this", "6" ] }, "then": 6 },
{ "case": { $eq: [ "$$this", "7" ] }, "then": 7 },
{ "case": { $eq: [ "$$this", "8" ] }, "then": 8 },
{ "case": { $eq: [ "$$this", "9" ] }, "then": 9 },
{ "case": { $eq: [ "$$this", "0" ] }, "then": 0 },
{ "case": { $and: [ { $eq: [ "$$this", "-" ] }, { $eq: [ "$$value.n", 0 ] } ] }, "then": "-" }, // we allow a minus sign at the start
{ "case": { $eq: [ "$$this", "." ] }, "then": "." }
],
default: null // marker to skip the current character
}
}
},
"in": {
$switch: {
"branches": [
{
"case": { $eq: [ "$$n", "-" ] },
"then": { // handle negative numbers
"sign": -1, // set sign to -1, the rest stays untouched
"n": "$$value.n",
"div": "$$value.div",
"mult": "$$value.mult",
},
},
{
"case": { $eq: [ "$$n", null ] }, // null is the "ignore this character" marker
"then": "$$value" // no change to current value
},
{
"case": { $eq: [ "$$n", "." ] },
"then": { // handle decimals
"n": "$$value.n",
"sign": "$$value.sign",
"div": 10, // from the decimal separator "." onwards, we start dividing new numbers by some divisor which starts at 10 initially
"mult": 1, // and we stop multiplying the current value by ten
},
},
],
"default": {
"n": {
$add: [
{ $multiply: [ "$$value.n", "$$value.mult" ] }, // multiply the already parsed number by 10 because we're moving one step to the right or by one once we're hitting the decimals section
{ $divide: [ "$$n", { $ifNull: [ "$$value.div", 1 ] } ] } // add the respective numerical value of what we look at currently, potentially divided by a divisor
]
},
"sign": "$$value.sign",
"div": { $multiply: [ "$$value.div" , 10 ] },
"mult": "$$value.mult"
}
}
}
}
}
}
}
}
}, {
$addFields: { // fix sign
"moop": { $multiply: [ "$moop.n", "$moop.sign" ] }
}
})
Я, конечно, не рекламирую это как пчелиные колени или что-то еще, и это может иметь серьезные последствия для больших наборов данных над решениями на основе клиента, но могут быть случаи, когда это пригодится...
Вышеуказанный конвейер преобразует следующие документы:
{ "moop": "12345" } --> { "moop": 12345 }
а также
{ "moop": "123.45" } --> { "moop": 123.45 }
а также
{ "moop": "-123.45" } --> { "moop": -123.45 }
а также
{ "moop": "2018-01-03" } --> { "moop": 20180103.0 }
Ответ 7
Три вещи нужно ухаживать за:
- parseInt() будет хранить двойной тип данных в mongodb. Используйте новый NumberInt (строка).
- В командной строке Mongo для массового использования выход не будет работать. Не добавляйте "yield".
- Если вы уже изменили строку на double на parseInt(). Похоже, у вас нет возможности напрямую изменить тип на int. Решение немного подключено: сначала измените double на строку, а затем вернитесь к int с помощью нового NumberInt().
Ответ 8
Он должен быть сохранен. Это должно быть так:
db. my_collection.find({}).forEach(function(theCollection) {
theCollection.moop = parseInt(theCollection.moop);
db.my_collection.save(theCollection);
});
Ответ 9
Сортировка - вот что вам нужно:
db.collectionName.find().sort({PartnerID: 1}).collation({locale: "en_US", numericOrdering: true})
Ответ 10
Пытаться:
"TimeStamp":{$toDecimal: { $toDate:"$Datum"}}
Ответ 11
Если вы можете редактировать все документы в совокупности:
"TimeStamp": {$toDecimal: {$toDate: "$Your Date"}}
А для клиента вы задаете запрос:
Date.parse("Your date".toISOString())
Это то, что заставляет вас работать с ISODate.
Ответ 12
db.user.find(). ToArray(). фильтр (а => a.age> 40)