我有机场的mongodb收集,我需要准备一些地理空间查询。
这是一个来自这个系列的简单文件:
{ "_id" : ObjectId("528e8134556062edda12ffe6"), "id" : 6523, "ident" : "00A", "type" : "heliport", "name" : "Total Rf Heliport", "latitude_deg" : 40.07080078125, "longitude_deg" : -74.9336013793945, "elevation_ft" : 11, "continent" : "NA", "iso_country" : "US", "iso_region" : "US-PA", "municipality" : "Bensalem", "scheduled_service" : "no", "gps_code" : "00A", "iata_code" : "", "local_code" : "00A", "home_link" : "", "wikipedia_link" : "", "keywords" : "" }
我需要将所有文档更改为:
{ "_id": ObjectId("528e8134556062edda12ffe6"), "id" : 6523, "ident" : "00A", "type" : "heliport", "name" : "Total Rf Heliport", "longitude_deg" : 17.27, "latitude_deg" : 52.22, "loc" : { "type" : "Point", "coordinates" : [ 17.27, 52.22 ] }, ... }
这是一个适合它的简单javascript:
var cursor = db.airports.find() cursor.forEach(function(input) { x = input.latitude_deg; y = input.longitude_deg; id = input._id; db.airports.update({"_id":id},{$set:{"loc":{"type":"Point","coordinates":[y,x]}}}); });
但我必须在java中创建相同的程序,即使我尝试我也不会出现这种情况。
如果有人能引导我以某种方式解决?
提前致谢,抱歉我的英语很差!
答案 0 :(得分:1)
下面是我创建的一个示例应用程序,展示了如何使用MongoDB Inc.驱动程序(旧版)和Asynchronous Java Driver执行此操作。
对于异步驱动程序,我展示了两种不同的方法:同步和异步。异步版本的主要驱动程序是,同步/遗留情况必须等待每个更新完成。在异步模型中,您可以继续完成有意义的工作,同时在“后台”中进行文档获取和更新流。
HTH, 罗布。
P.S。完全披露:我在使用异步Java驱动程序。
package geojson.sof20181050;
import java.io.IOException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.concurrent.Phaser;
import java.util.concurrent.atomic.AtomicLong;
import com.allanbank.mongodb.Callback;
import com.allanbank.mongodb.Durability;
import com.allanbank.mongodb.MongoClient;
import com.allanbank.mongodb.MongoCollection;
import com.allanbank.mongodb.MongoFactory;
import com.allanbank.mongodb.StreamCallback;
import com.allanbank.mongodb.bson.Document;
import com.allanbank.mongodb.bson.Element;
import com.allanbank.mongodb.bson.NumericElement;
import com.allanbank.mongodb.bson.builder.BuilderFactory;
import com.allanbank.mongodb.bson.builder.DocumentBuilder;
import com.allanbank.mongodb.builder.Find;
import com.allanbank.mongodb.builder.GeoJson;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.mongodb.MongoClientURI;
import com.mongodb.WriteConcern;
/**
* ConvertToGeoJSON provides a solution to a Stack Overflow question on how to
* conver a document containing a latitude and longitude value to contain a
* GeoJSON formated value instead. From the Stack Overflow question: Each input
* document looks like: <blockquote>
*
* <pre>
* <code>
* {
* "_id" : ObjectId("528e8134556062edda12ffe6"),
* "id" : 6523,
* "ident" : "00A",
* "type" : "heliport",
* "name" : "Total Rf Heliport",
* "latitude_deg" : 40.07080078125,
* "longitude_deg" : -74.9336013793945,
* "elevation_ft" : 11,
* "continent" : "NA",
* "iso_country" : "US",
* "iso_region" : "US-PA",
* "municipality" : "Bensalem",
* "scheduled_service" : "no",
* "gps_code" : "00A",
* "iata_code" : "",
* "local_code" : "00A",
* "home_link" : "",
* "wikipedia_link" : "",
* "keywords" : ""
* }
* </code>
* </pre>
*
* </blockquote>
* <p>
* We want the resulting document to look like:<blockquote>
*
* <pre>
* <code>
* {
* "_id": ObjectId("528e8134556062edda12ffe6"),
* "id" : 6523,
* "ident" : "00A",
* "type" : "heliport",
* "name" : "Total Rf Heliport",
* "longitude_deg" : 17.27,
* "latitude_deg" : 52.22,
* "loc" : {
* "type" : "Point",
* "coordinates" : [
* 17.27,
* 52.22
* ]
* },
* ...
* }
* </code>
* </pre>
*
* </blockquote>
* </p>
*
* @see <a
* href="http://stackoverflow.com/questions/20181050/how-do-i-update-fields-of-documents-in-mongo-db-using-the-java-to-geojson-format">Stack
* Overflow Question</a>
*/
public class ConvertToGeoJSON {
/**
* The handle to the MongoDB client. We assume MongoDB is running on your
* machine on the default port of 27017.
*/
protected final static MongoClient client;
/** The collection we will be using. */
protected final static MongoCollection theCollection;
/** The URI for connecting to MongoDB. */
protected static final String URI;
static {
URI = "mongodb://localhost:27017/";
client = MongoFactory.createClient(URI);
theCollection = client.getDatabase("db").getCollection("collection");
}
/**
* See the class Javadoc for a description of the problem..
*
* @param args
* Command line arguments. Ignored.
* @throws InterruptedException
* If waiting for the callback to complete is interrupted.
* @throws IOException
* On a failure to close the client.
*/
public static void main(final String[] args) throws InterruptedException,
IOException {
try {
// We can perform this operation two way. Synchronously and via
// streaming. We will provide an example of both. Change these
// variables to switch between them.
final boolean doLegacy = false;
final boolean doSynchronous = false;
if (doLegacy) {
doLegacy();
}
else if (doSynchronous) {
doSynchronously();
}
else {
doAsynchronously();
}
}
finally {
// Always close the client!
client.close();
}
}
/**
* Performs the document updates asynchronously. This method uses a pair of
* callbacks to perform the updates. The first receives the stream of
* documents in the collection and prepares and sends the update for each
* document. The second receives the results of the update and checks for
* errors.
* <p>
* Neither callback performs robust error handling but could be easily
* modified to retry operations etc.
* </p>
* <p>
* The advantage of this version is that the stream of updates can be
* handled concurrently with the iteration over the results in each batch of
* documents in the collection. This should result in a significant
* reduction in the wall clock time for processing the collections time.
* </p>
* <p>
* We use {@link Phaser} instances to track when we are waiting for
* asynchronous operations so the main thread knows when to terminate the
* application.
* </p>
*
* @throws InterruptedException
* If waiting for the callback to complete is interrupted.
*/
protected static void doAsynchronously() throws InterruptedException {
// Execute the query to find all of the documents and stream
// them to the callback. Have that callback update the document
// asynchronously.
final Phaser finished = new Phaser(1); // Parent.
final AtomicLong updates = new AtomicLong(0);
final StreamCallback<Document> streamCallback = new StreamCallback<Document>() {
// Child Phaser for the complete stream and all updates.
final Phaser streamFinished = new Phaser(finished, 1);
@Override
public void callback(final Document doc) {
final Element id = doc.get("_id");
final NumericElement lat = doc.get(NumericElement.class,
"latitude_deg");
final NumericElement lon = doc.get(NumericElement.class,
"longitude_deg");
final DocumentBuilder query = BuilderFactory.start();
query.add(id);
final DocumentBuilder update = BuilderFactory.start();
update.push("$set").add(
"loc",
GeoJson.point(GeoJson.p(lon.getDoubleValue(),
lat.getDoubleValue())));
final Callback<Long> updateCallback = new Callback<Long>() {
// Child Phaser for the update.
final Phaser updateFinished = new Phaser(streamFinished, 1);
@Override
public void callback(final Long result) {
// All done. Notify the stream.
updates.addAndGet(result);
updateFinished.arriveAndDeregister();
}
@Override
public void exception(final Throwable thrown) {
System.err.printf("Update of {0} failed.\n", id);
thrown.printStackTrace();
callback(null);
}
};
theCollection.updateAsync(updateCallback, query, update,
Durability.ACK);
}
@Override
public synchronized void done() {
streamFinished.arriveAndDeregister();
}
@Override
public void exception(final Throwable thrown) {
thrown.printStackTrace();
done();
}
};
// Now to kick off the processing.
theCollection.streamingFind(streamCallback, Find.ALL);
// Need to wait for the stream and updates to finish.
finished.arriveAndAwaitAdvance();
System.out.printf("Updated %d documents asynchronously.%n",
updates.get());
}
/**
* Performs the document updates using the legacy driver.
* <p>
* The main draw back here (other than those discussed in
* {@link #doSynchronously()}) is the difficulty creating the GeoJSON
* documents.
* </p>
*
* @throws UnknownHostException
* On an invalid URI.
*/
protected static void doLegacy() throws UnknownHostException {
// Execute the query to find all of the documents and then
// update them.
final com.mongodb.MongoClient legacyClient = new com.mongodb.MongoClient(
new MongoClientURI(URI));
final com.mongodb.DBCollection legacyCollection = legacyClient.getDB(
theCollection.getDatabaseName()).getCollection(
theCollection.getName());
try {
int count = 0;
for (final DBObject doc : legacyCollection.find()) {
final Object id = doc.get("_id");
final Number lat = (Number) doc.get("latitude_deg");
final Number lon = (Number) doc.get("longitude_deg");
final BasicDBObject query = new BasicDBObject();
query.append("_id", id);
final ArrayList<Double> coordinates = new ArrayList<>();
coordinates.add(lon.doubleValue());
coordinates.add(lat.doubleValue());
final BasicDBObject geojson = new BasicDBObject("type", "Point");
geojson.append("coordinates", coordinates);
final BasicDBObject set = new BasicDBObject("loc", geojson);
final BasicDBObject update = new BasicDBObject("$set", set);
legacyCollection.update(query, update, /* upsert= */false,
/* multi= */false, WriteConcern.ACKNOWLEDGED);
count += 1;
}
System.out.printf("Updated %d documents via the legacy driver.%n",
count);
}
finally {
// Always close the client.
legacyClient.close();
}
}
/**
* Performs the document updates synchronously.
* <p>
* While this version is conceptually easier to implement the need to wait
* for each update to complete before processing the next document has a
* severe impact on the wall clock time required to complete the update all
* but the smallest of documents.
*/
protected static void doSynchronously() {
// Execute the query to find all of the documents and then
// update them.
int count = 0;
for (final Document doc : theCollection.find(Find.ALL)) {
final Element id = doc.get("_id");
final NumericElement lat = doc.get(NumericElement.class,
"latitude_deg");
final NumericElement lon = doc.get(NumericElement.class,
"longitude_deg");
final DocumentBuilder query = BuilderFactory.start();
query.add(id);
final DocumentBuilder update = BuilderFactory.start();
update.push("$set").add(
"loc",
GeoJson.point(GeoJson.p(lon.getDoubleValue(),
lat.getDoubleValue())));
theCollection.update(query, update, Durability.ACK);
count += 1;
}
System.out.printf("Updated %d documents synchronously.%n",
count);
}
}
答案 1 :(得分:0)
感谢您的回答。 我使用了传统的驱动程序方法和一些改变代码,我得到了:
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Numberat
代码看起来像这样:
import java.io.IOException;
import java.util.ArrayList;
import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.mongodb.MongoClient;
import com.mongodb.WriteConcern;
public class Geo {
public static void main(final String[] args) throws InterruptedException,
IOException {
MongoClient mongoClient = new MongoClient("127.0.0.1", 27017);
DB db = mongoClient.getDB("test");
DBCollection coll = db.getCollection("airports2");
try {
int count = 0;
for (final DBObject doc : coll.find()) {
final Object id = doc.get("_id");
final Number lat = Double.parseDouble(doc.get("latitude_deg").toString());
final Number lon = Double.parseDouble(doc.get("longitude_deg").toString());
final BasicDBObject query = new BasicDBObject();
query.append("_id", id);
final ArrayList coordinates = new ArrayList();
coordinates.add(lon.doubleValue());
coordinates.add(lat.doubleValue());
final BasicDBObject geojson = new BasicDBObject("type", "Point");
geojson.append("coordinates", coordinates);
final BasicDBObject set = new BasicDBObject("loc", geojson);
final BasicDBObject update = new BasicDBObject("$set", set);
coll.update(query, update, /* upsert= */false,
/* multi= */false, WriteConcern.ACKNOWLEDGED);
count += 1;
}
System.out.printf("Updated %d documents via the legacy driver.%n",
count);
}
finally {
// Always close the client.
mongoClient.close();
}
}
}
它有效,但更新文件的数量与db.airports.count()相同,这让我很担心。
我正在使用集合airports。