Tìm kiếm văn bản (Mới)

Chọn nền tảng: Android iOS JavaScript Dịch vụ web
Nhà phát triển ở Khu vực kinh tế Châu Âu (EEA)

Tính năng Tìm kiếm văn bản (Mới) lấy một cụm từ tìm kiếm dạng văn bản và trả về danh sách các địa điểm phù hợp.

Tìm kiếm văn bản (Mới) trả về thông tin về một nhóm địa điểm dựa trên một chuỗi, chẳng hạn như "pizza ở New York" hoặc "cửa hàng giày gần Ottawa" hoặc "123 Main Street". Dịch vụ này sẽ phản hồi bằng danh sách các địa điểm khớp với chuỗi văn bản và mọi độ lệch vị trí đã được đặt. Tính năng Tìm kiếm văn bản (Mới) cho phép bạn tìm kiếm địa điểm theo loại, lọc bằng các tiêu chí như giờ hoạt động và điểm xếp hạng, đồng thời hạn chế hoặc thiên vị kết quả cho một vị trí cụ thể.

Để sử dụng Tìm kiếm văn bản (Mới), bạn phải bật "Places API (Mới)" trong dự án trên đám mây của mình trên Google Cloud. Hãy xem phần Bắt đầu để biết thông tin chi tiết.

Tìm địa điểm theo cụm từ tìm kiếm dạng văn bản

Gọi searchByText để trả về danh sách địa điểm từ một cụm từ tìm kiếm dạng văn bản hoặc số điện thoại. Chỉ định các tham số tìm kiếm bằng một yêu cầu, sau đó gọi searchByText(). Kết quả được trả về dưới dạng danh sách đối tượng Place, từ đó bạn có thể lấy Place Details. Đoạn mã sau đây cho thấy ví dụ về một yêu cầu và lệnh gọi đến searchByText:

TypeScript

const request = {
    textQuery: query,
    fields: ['displayName', 'location', 'businessStatus'],
    includedType: '', // Restrict query to a specific type (leave blank for any).
    useStrictTypeFiltering: true,
    locationRestriction: map.getBounds(),
    isOpenNow: true,
    language: 'en-US',
    maxResultCount: 8,
    minRating: 1, // Specify a minimum rating.
    region: 'us',
};

const { places } = await Place.searchByText(request);

JavaScript

const request = {
    textQuery: query,
    fields: ['displayName', 'location', 'businessStatus'],
    includedType: '', // Restrict query to a specific type (leave blank for any).
    useStrictTypeFiltering: true,
    locationRestriction: map.getBounds(),
    isOpenNow: true,
    language: 'en-US',
    maxResultCount: 8,
    minRating: 1, // Specify a minimum rating.
    region: 'us',
};

const { places } = await Place.searchByText(request);
  • Chỉ định một cụm từ tìm kiếm dạng văn bản hoặc số điện thoại để tìm kiếm bằng tham số textQuery.
  • Sử dụng tham số fields (bắt buộc) để chỉ định danh sách được phân tách bằng dấu phẩy của một hoặc nhiều trường dữ liệu theo kiểu viết lạc đà (viết liền và phân tách bằng chữ cái viết hoa). Các trường bạn yêu cầu sẽ đặt giá cho yêu cầu. Xem phần Chọn trường dữ liệu.
  • Sử dụng tham số includedType để chỉ trả về kết quả của loại được chỉ định.
  • Sử dụng locationBias hoặc locationRestriction để thiên vị hoặc hạn chế kết quả tìm kiếm văn bản của bạn cho một khu vực cụ thể.
Xem danh sách đầy đủ các thuộc tính.

Nếu cụm từ tìm kiếm có chứa số điện thoại, bạn nên đặt tham số khu vực. Ví dụ: nếu bạn sử dụng số điện thoại để tìm kiếm một địa điểm ở Nhật Bản và miền yêu cầu là jp, bạn phải đặt tham số region thành "jp". Nếu region bị bỏ qua trong yêu cầu, thì API sẽ mặc định là khu vực Hoa Kỳ ("us").

Kết quả được trả về dưới dạng danh sách các đối tượng Place, từ đó bạn có thể lấy Place Details.

Ví dụ

Ví dụ sau đây gọi searchByText bằng văn bản truy vấn được cung cấp, sau đó điền vào bản đồ các điểm đánh dấu có thể nhấp để hiển thị kết quả.

TypeScript

let map: google.maps.Map;
let markers: Record<string, google.maps.marker.AdvancedMarkerElement> = {};
let infoWindow: google.maps.InfoWindow;

async function init() {
    const [{ Map, InfoWindow }, { ControlPosition }] = await Promise.all([
        google.maps.importLibrary('maps'),
        google.maps.importLibrary('core'),
    ]);

    const center = { lat: 37.4161493, lng: -122.0812166 };
    map = new Map(document.getElementById('map')!, {
        center,
        zoom: 11,
        mapTypeControl: false,
        mapId: 'DEMO_MAP_ID',
    });

    const textInput = document.getElementById('text-input') as HTMLInputElement;
    const textInputButton = document.getElementById(
        'text-input-button'
    ) as HTMLButtonElement;
    const card = document.getElementById('text-input-card')!;
    map.controls[ControlPosition.TOP_LEFT].push(card);

    textInputButton.addEventListener('click', () => {
        void findPlaces(textInput.value);
    });

    textInput.addEventListener('keydown', (event) => {
        if (event.key === 'Enter') {
            void findPlaces(textInput.value);
        }
    });

    infoWindow = new InfoWindow();
}

async function findPlaces(query: string) {
    const [{ Place }, { AdvancedMarkerElement }] = await Promise.all([
        google.maps.importLibrary('places'),
        google.maps.importLibrary('marker'),
    ]);

    const request = {
        textQuery: query,
        fields: ['displayName', 'location', 'businessStatus'],
        includedType: '', // Restrict query to a specific type (leave blank for any).
        useStrictTypeFiltering: true,
        locationRestriction: map.getBounds(),
        isOpenNow: true,
        language: 'en-US',
        maxResultCount: 8,
        minRating: 1, // Specify a minimum rating.
        region: 'us',
    };

    const { places } = await Place.searchByText(request);

    if (places.length) {
        const { LatLngBounds } = await google.maps.importLibrary('core');
        const bounds = new LatLngBounds();

        // First remove all existing markers.
        for (const id in markers) {
            markers[id].map = null;
        }
        markers = {};

        // Loop through and get all the results.
        places.forEach((place: google.maps.places.Place) => {
            const marker = new AdvancedMarkerElement({
                map,
                position: place.location,
                title: place.displayName,
                gmpClickable: true,
            });
            markers[place.id] = marker;

            marker.addEventListener('gmp-click', () => {
                map.panTo(place.location!);
                updateInfoWindow(place.displayName!, place.id, marker);
            });

            if (place.location != null) {
                bounds.extend(place.location);
            }
        });

        map.fitBounds(bounds);
    } else {
        console.log('No results');
    }
}

// Helper function to create an info window.
function updateInfoWindow(
    title: string | Element | null,
    content: string | Element | null,
    anchor: google.maps.marker.AdvancedMarkerElement
) {
    infoWindow.setContent(content);
    infoWindow.setHeaderContent(title);
    infoWindow.open({
        map,
        anchor,
        shouldFocus: false,
    });
}

void init();

JavaScript

let map;
let markers = {};
let infoWindow;

async function init() {
    const [{ Map, InfoWindow }, { ControlPosition }] = await Promise.all([
        google.maps.importLibrary('maps'),
        google.maps.importLibrary('core'),
    ]);

    const center = { lat: 37.4161493, lng: -122.0812166 };
    map = new Map(document.getElementById('map'), {
        center,
        zoom: 11,
        mapTypeControl: false,
        mapId: 'DEMO_MAP_ID',
    });

    const textInput = document.getElementById('text-input');
    const textInputButton = document.getElementById('text-input-button');
    const card = document.getElementById('text-input-card');
    map.controls[ControlPosition.TOP_LEFT].push(card);

    textInputButton.addEventListener('click', () => {
        void findPlaces(textInput.value);
    });

    textInput.addEventListener('keydown', (event) => {
        if (event.key === 'Enter') {
            void findPlaces(textInput.value);
        }
    });

    infoWindow = new InfoWindow();
}

async function findPlaces(query) {
    const [{ Place }, { AdvancedMarkerElement }] = await Promise.all([
        google.maps.importLibrary('places'),
        google.maps.importLibrary('marker'),
    ]);

    const request = {
        textQuery: query,
        fields: ['displayName', 'location', 'businessStatus'],
        includedType: '', // Restrict query to a specific type (leave blank for any).
        useStrictTypeFiltering: true,
        locationRestriction: map.getBounds(),
        isOpenNow: true,
        language: 'en-US',
        maxResultCount: 8,
        minRating: 1, // Specify a minimum rating.
        region: 'us',
    };

    const { places } = await Place.searchByText(request);

    if (places.length) {
        const { LatLngBounds } = await google.maps.importLibrary('core');
        const bounds = new LatLngBounds();

        // First remove all existing markers.
        for (const id in markers) {
            markers[id].map = null;
        }
        markers = {};

        // Loop through and get all the results.
        places.forEach((place) => {
            const marker = new AdvancedMarkerElement({
                map,
                position: place.location,
                title: place.displayName,
                gmpClickable: true,
            });
            markers[place.id] = marker;

            marker.addEventListener('gmp-click', () => {
                map.panTo(place.location);
                updateInfoWindow(place.displayName, place.id, marker);
            });

            if (place.location != null) {
                bounds.extend(place.location);
            }
        });

        map.fitBounds(bounds);
    } else {
        console.log('No results');
    }
}

// Helper function to create an info window.
function updateInfoWindow(title, content, anchor) {
    infoWindow.setContent(content);
    infoWindow.setHeaderContent(title);
    infoWindow.open({
        map,
        anchor,
        shouldFocus: false,
    });
}

void init();

CSS

/* 
 * Always set the map height explicitly to define the size of the div element
 * that contains the map. 
 */
#map {
    height: 100%;
}

/* 
 * Optional: Makes the sample page fill the window. 
 */
html,
body {
    height: 100%;
    margin: 0;
    padding: 0;
}

#text-input-card {
    width: 25%;
    background-color: #fff;
    border-radius: 5px;
    box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px;
    margin: 10px;
    padding: 5px;
    font-family: Roboto, sans-serif;
    font-size: large;
    font-weight: bold;
}

#text-input {
    width: 100%;
    padding: 10px;
    margin: 0;
    box-sizing: border-box;
}

#text-input-button {
    display: inline-block;
    margin-top: 0.5rem;
    width: auto;
    padding: 0.6rem 0.75rem;
    font-size: 0.875rem;
    font-weight: 500;
    color: #fff;
    background-color: #2563eb;
    border: none;
    border-radius: 0.375rem;
    cursor: pointer;
    transition: background-color 0.15s ease-in-out;
    text-align: center;
}

HTML

<html>
    <head>
        <title>Text Search</title>

        <link rel="stylesheet" type="text/css" href="./style.css" />
        <script type="module" src="./index.js"></script>
        <script>
            // prettier-ignore
            (g=>{var h,a,k,p="The Google Maps JavaScript API",c="google",l="importLibrary",q="__ib__",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement("script"));e.set("libraries",[...r]+"");for(k in g)e.set(k.replace(/[A-Z]/g,t=>"_"+t[0].toLowerCase()),g[k]);e.set("callback",c+".maps."+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+" could not load."));a.nonce=m.querySelector("script[nonce]")?.nonce||"";m.head.append(a)}));d[l]?console.warn(p+" only loads once. Ignoring:",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))})({
                key: "GOOGLE_MAPS_API_KEY"
            });
        </script>
    </head>
    <body>
        <div id="text-input-card">
            <input
                type="text"
                id="text-input"
                placeholder="Search for a place" />
            <input type="button" id="text-input-button" value="Search" />
        </div>
        <div id="map"></div>
    </body>
</html>

Chọn trường dữ liệu

Các trường bạn yêu cầu sẽ đặt giá cho mỗi yêu cầu tìm kiếm. Yêu cầu được tính phí một lần, theo SKU cao nhất trong số các trường được yêu cầu. Ví dụ: việc thêm reviews vào fields sẽ tính phí toàn bộ yêu cầu dưới dạng Tìm kiếm văn bản Enterprise + Atmosphere.

Để giữ yêu cầu tìm kiếm ở mức Text Search Pro, chỉ yêu cầu những trường mà kết quả tìm kiếm của bạn hiển thị, chẳng hạn như id, displayName, formattedAddress và location.

Khi người dùng chọn một địa điểm, hãy gọi fetchFields() trên địa điểm đó để lấy các trường như reviews, nationalPhoneNumber, hoặc websiteURI. Mỗi lệnh gọi fetchFields() là một yêu cầu Place Details riêng biệt, được tính phí theo SKU cao nhất trong số các trường mà lệnh gọi đó yêu cầu. Việc phương pháp này có giúp tiết kiệm chi phí hay không phụ thuộc vào số lượng địa điểm mà người dùng mở.

Các trường kích hoạt từng SKU

  • Các trường sau đây sẽ kích hoạt SKU Tìm kiếm văn bản cơ bản (Chỉ mã nhận dạng):

    id

  • Các trường sau đây sẽ kích hoạt SKU Tìm kiếm văn bản Pro:

    accessibilityOptions
    addressComponents
    adrFormatAddress
    businessStatus
    displayName
    formattedAddress
    googleMapsUri
    iconBackgroundColor
    location
    photos
    plusCode
    svgIconMaskURI
    types
    utcOffsetMinutes
    viewport

  • Các trường sau đây kích hoạt SKU Tìm kiếm văn bản cho doanh nghiệp:

    internationalPhoneNumber
    nationalPhoneNumber
    priceLevel
    rating
    regularOpeningHours
    userRatingCount
    websiteURI

  • Các trường sau đây sẽ kích hoạt SKU Tìm kiếm văn bản Enterprise + Atmosphere:

    allowsDogs
    editorialSummary
    evChargeOptions
    fuelOptions
    hasCurbsidePickup
    hasDelivery
    hasDineIn
    hasLiveMusic
    hasMenuForChildren
    hasOutdoorSeating
    hasRestroom
    hasTakeout
    hasWifi
    isGoodForChildren
    isGoodForGroups
    isGoodForWatchingSports
    isReservable
    parkingOptions
    paymentOptions
    reviews
    servesBeer
    servesBreakfast
    servesBrunch
    servesCocktails
    servesCoffee
    servesDessert
    servesDinner
    servesLunch
    servesVegetarianFood
    servesWine

Để biết SKU của từng trường, hãy xem phần Trường dữ liệu của lớp Địa điểm.